From 7c52490f026d29168bd5aede605afd61e4774106 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:54:44 +0000 Subject: [PATCH 01/41] Bump schneegans/dynamic-badges-action from 1.6.0 to 1.7.0 Bumps [schneegans/dynamic-badges-action](https://github.com/schneegans/dynamic-badges-action) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/schneegans/dynamic-badges-action/releases) - [Changelog](https://github.com/Schneegans/dynamic-badges-action/blob/master/changelog.md) - [Commits](https://github.com/schneegans/dynamic-badges-action/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: schneegans/dynamic-badges-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/badge.yaml | 6 +++--- .github/workflows/call-e2e.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/badge.yaml b/.github/workflows/badge.yaml index a5389ccb..9517ede0 100644 --- a/.github/workflows/badge.yaml +++ b/.github/workflows/badge.yaml @@ -42,7 +42,7 @@ jobs: - name: Create Lines-of-Code-Badge if: ${{ env.BADGE_CODELINE_ID != '' }} - uses: schneegans/dynamic-badges-action@v1.6.0 + uses: schneegans/dynamic-badges-action@v1.7.0 with: auth: ${{ secrets.WELAN_PAT }} gistID: ${{ env.BADGE_CODELINE_ID }} @@ -53,7 +53,7 @@ jobs: - name: Create Comments-Badge if: ${{ env.BADGE_COMMENT_LINE != '' }} - uses: schneegans/dynamic-badges-action@v1.6.0 + uses: schneegans/dynamic-badges-action@v1.7.0 with: auth: ${{ secrets.WELAN_PAT }} gistID: ${{ env.BADGE_COMMENT_LINE }} @@ -66,7 +66,7 @@ jobs: - name: Create E2E-Badge if: ${{ env.BADGE_E2ECOVER_ID != '' }} - uses: schneegans/dynamic-badges-action@v1.6.0 + uses: schneegans/dynamic-badges-action@v1.7.0 with: auth: ${{ secrets.E2ECOVER_II2DAY }} gistID: ${{ env.BADGE_E2ECOVER_ID }} diff --git a/.github/workflows/call-e2e.yaml b/.github/workflows/call-e2e.yaml index e5a687fb..a839eb24 100644 --- a/.github/workflows/call-e2e.yaml +++ b/.github/workflows/call-e2e.yaml @@ -191,7 +191,7 @@ jobs: - name: Update Badge if: ${{ env.RUN_PERFORMANCE_RESULT != '' && inputs.ipfamily == 'dual' && env.PERFORMANCE_BADGE_ID != '' }} - uses: schneegans/dynamic-badges-action@v1.6.0 + uses: schneegans/dynamic-badges-action@v1.7.0 with: auth: ${{ secrets.WELAN_PAT }} gistID: ${{ env.PERFORMANCE_BADGE_ID }} From bd25c0ea717e83c03b59575d103c52133f76e994 Mon Sep 17 00:00:00 2001 From: Jeanine-tw <76861242+Jeanine-tw@users.noreply.github.com> Date: Wed, 1 Nov 2023 18:08:46 +0800 Subject: [PATCH 02/41] doc: add en doc about netreach --- docs/reference/netdns.md | 2 +- docs/reference/netreach.md | 152 +++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/docs/reference/netdns.md b/docs/reference/netdns.md index 9ad88162..1c6ffe75 100644 --- a/docs/reference/netdns.md +++ b/docs/reference/netdns.md @@ -105,7 +105,7 @@ spec: |-----------|-------------|--------------------------------------------|---------|-------|------| | agentSpec | task execution agent configuration | [agentSpec](./apphttphealthy.md#agentspec) | optional | | | | schedule | Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | optional | | | -| request | Request configuration for destination address | [request](./netdns-zh_CN.md#Request) | Optional | | | +| request | Request configuration for destination address | [request](./netdns.md#request) | Optional | | | | target | Request target settings | [target](./apphttphealthy.md#target) | Optional | | | | expect | Task success condition judgment | [expect](./apphttphealthy.md#expect) | Optional | | | diff --git a/docs/reference/netreach.md b/docs/reference/netreach.md index 23276677..84b9fa5b 100644 --- a/docs/reference/netreach.md +++ b/docs/reference/netreach.md @@ -1,3 +1,155 @@ # NetReach [**简体中文**](./netreach-zh_CN.md) | **English** + +## Basic description + +For this kind of task, kdoctor-controller will generate corresponding [agent](../concepts/runtime.md) and other resources, and each agent pod sends http requests to each other with the request address of each agent's pod ip, service ip, ingress ip and so on, and obtains the success rate and average latency. It can specify the success condition to determine whether the result is successful or not. And, detailed reports can be obtained through the aggregation API. + +## NetReach example + +```yaml +apiVersion: kdoctor.io/v1beta1 +kind: NetReach +metadata: + name: netreach +spec: + agentSpec: + hostNetwork: false + kind: DaemonSet + terminationGracePeriodMinutes: 60 + expect: + meanAccessDelayInMs: 1500 + successRate: 1 + request: + durationInSecond: 10 + perRequestTimeoutInMS: 1000 + qps: 10 + schedule: + roundNumber: 1 + roundTimeoutMinute: 1 + schedule: 0 1 + target: + clusterIP: true + enableLatencyMetric: false + endpoint: true + ingress: true + ipv4: true + ipv6: true + loadBalancer: true + multusInterface: false + nodePort: true +status: + doneRound: 1 + expectedRound: 1 + finish: true + history: + - deadLineTimeStamp: "2023-07-28T09:59:12Z" + duration: 15.462579243s + endTimeStamp: "2023-07-28T09:58:27Z" + expectedActorNumber: 2 + failedAgentNodeList: + - kdoctor-worker + - kdoctor-control-plane + failureReason: some agents failed + notReportAgentNodeList: [] + roundNumber: 1 + startTimeStamp: "2023-07-28T09:58:12Z" + status: fail + succeedAgentNodeList: [] + lastRoundStatus: fail +``` + +## NetReach Definition + +### Metadata + +| fields | description | structure | validation | +|-----|---------------|--------|-----| +| name | Name of the NetReach resource | string | required | + +### Spec + +| fields | description | structure | validation | take values | default | +|-----------|-------------|--------------------------------------------|---------|-------|------| +| agentSpec | task execution agent configuration | [agentSpec](./apphttphealthy.md#agentspec) | optional | | | +| schedule |Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | optional | | | +| request |Request configuration for destination address | [request](./netdns.md#request) | Optional | | | +| target | Request target settings | [target](./apphttphealthy.md#target) | Optional | | | +| expect |Task success condition judgment | [expect](./apphttphealthy.md#expect) | Optional | | | + + +#### AgentSpec + +| Fields | Description | Structure | Validation | Values | Default | +|-------------------------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----|----------------------|-------------------------------| +| annotation | annotation of agent workload | map[string]string | optional | | | | +| kind | type of agent workload | string | optional | Deployment, DaemonSet | DaemonSet | +| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | optional | greater than or equal to 0 | 0 | +| affinity | agent workload affinity | labelSelector | optional | | | +| env | agent workload environment variable | env | optional | | | | hostNetwork | agent +| hostNetwork | agent Whether or not the workload uses the host network | bool | optional | true, false | false | +| resources | agent workload resource usage configuration | resources | optional | | limit cpu:1000m,memory:1024Mi | +| terminationGracePeriodMinutes | agent How many minutes after a workload completes a task before it terminates | int | optional | greater than or equal to 0 | 60 | + + +#### Schedule + +| Fields | Description | Structure | Validation | Values | Defaults | +|--------------------|---------------------------------------|--------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| +| roundNumber | task execution rounds | int | optional | greater than or equal to -1, -1 means permanent, greater than 0 means rounds will be executed | 1 | schedule | task execution time, execution time should be less than roundTimeoutMinute | string | optional | Support linux crontab and interval method.
[linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
Interval method: written in "M N" format, M takes the value of a number, indicating how many minutes after opening the task, N takes the value of a number, indicating how many minutes between each round of task execution, for example, "0 1" means start the task immediately, each round of task interval 1min | "0 1" | roundTimeoutMininute +| roundTimeoutMinute | Task timeout, needs to be greater than durationInSecond and task execution time | int | optional | greater than or equal to 1 | 60 | + +#### Request + +| Fields | Description | Structure | Validation | Values | Defaults | +|------------------------|---------------------------------------|--------|-----|---------------|---------------| +| durationInSecond | Duration of request send pressure for each round of tasks less than roundTimeoutMinute | int | optional | greater than or equal to 1 | 2 | +| perRequestTimeoutInMS | timeout per request, not greater than durationInSecond | int | optional | greater than or equal to 1 | 500 | +| qps | requests per second per agent | int | optional | greater than or equal to 1 | 5 | + +> Note: When using agent requests, all agents make requests to the destination address, so the actual qps received by the server is equal to the number of agents * the set qps. + +#### Target + +| Fields | Descriptions | Structures | Validations | Values | Defaults | +|--------------------|-------------------------|--------|-----|------------|-------| +| clusterIP | Test cluster service's cluster ip | bool | Optional | true,false | true | +| endpoint | Test cluster pod endpoint | bool | Optional | true,false | true | +| multusInterface | Test cluster pod multus multi-NIC ip | bool | Optional | true,false | false | +| ipv4 | Test ipv4 | bool | Optional | true,false | true | +| ipv6 | Test ipv6 | bool | Optional | true,false | false | +| ingress | Test ingress address | bool | Optional | true,false | false | +| nodePort | test service node port | bool | Optional | true,false | true | +| enableLatencyMetric | Count demo distribution, when turned on it will increase memory usage | bool | Optional | true,false | false | + +#### Expect + +Task success condition, if the task result does not meet the expected condition, the task will fail. + +| fields | description | structure | validation | values | default | +| --------------------| ---------------------------------| -------| -----| --------| ------| +| meanAccessDelayInMs | meanAccessDelayInMs | The average delay, if the final result exceeds this value, the task will be judged as failed | int | optional | greater than or equal to 1 | 5000 | +| successRate | The success rate of the http request, if the final result is less than this value, the task will fail | float | optional | 0-1 | 1 | + +### status + +| fields | description | structure | values | +|--------------------|----------|------------------------------------------|-----------------| +| doneRound | number of completed task rounds | int | | +| expectedRound | number of rounds expected to be performed | int | | | +| finish | Whether the task is complete or not | bool | true, false | +| lastRoundStatus | lastRoundStatus | string | notstarted, on-going, succeed, fail | +| history | Task history | Element is [history](./apphttphealthy.md#history) array | | + +#### History + +| Fields | Description | Structure | Values | +| ----------------------------------|-----------------|--------------|--------------------------------| +| roundNumber | Task round number | int | | +| status | Task Status | string | notstarted, on-going, succeed, fail | +| startTimeStamp | startTimeStamp | startTimeStamp | string | | +| startTimeStamp | startTimeStamp | startTimeStamp | endTimeStamp | endTimeStamp | endTimeStamp | string | | +| deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadline | string | | +| failedAgentNodeList | failedAgentNodeList | Array of failed agents | string | | +| notReportAgentNodeList | agent who did not upload a task report | array of elements as string | | notReportAgentNodeList | agent who failed to upload a task report | array of elements as string| From c7f83bfaab41f39cf2346c976667f243140a061a Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 1 Nov 2023 16:16:55 +0800 Subject: [PATCH 03/41] add qps stats in report Signed-off-by: ii2day --- charts/crds/kdoctor.io_netdnses.yaml | 3 - .../apis/kdoctor.io/v1beta1/netdns_types.go | 6 +- .../v1beta1/zz_generated.deepcopy.go | 15 ---- pkg/k8s/apis/system/v1beta1/apphttphealthy.go | 14 ++-- pkg/k8s/apis/system/v1beta1/common_types.go | 11 +++ pkg/k8s/apis/system/v1beta1/netdns.go | 14 ++-- pkg/k8s/apis/system/v1beta1/netreach.go | 14 ++-- .../system/v1beta1/zz_generated.deepcopy.go | 36 ++++++++++ pkg/pluginManager/agentManager.go | 18 +++-- pkg/pluginManager/agentReconciler.go | 16 +++-- pkg/pluginManager/agentTools.go | 65 +++++++++++++++-- .../apphttphealthy/agentExecuteTask.go | 15 ++-- pkg/pluginManager/netdns/agentExecuteTask.go | 51 +++++++------- pkg/pluginManager/netdns/webhook.go | 6 +- .../netreach/agentExecuteTask.go | 15 ++-- pkg/pluginManager/types/types.go | 4 +- pkg/resource/resource.go | 70 ++++++++++++------- pkg/runningTask/runningTask.go | 65 +++++++++++++++++ test/docs/AppHttpHealth.md | 1 - test/e2e/apphttphealth/apphttphealth_test.go | 62 ---------------- test/e2e/common/tools.go | 11 +-- test/e2e/netdns/netdns_test.go | 45 ++++-------- test/e2e/runtime/runtime_test.go | 18 ++--- test/scripts/debugCluster.sh | 4 +- 24 files changed, 339 insertions(+), 240 deletions(-) create mode 100644 pkg/runningTask/runningTask.go diff --git a/charts/crds/kdoctor.io_netdnses.yaml b/charts/crds/kdoctor.io_netdnses.yaml index 59fb1284..e998989c 100644 --- a/charts/crds/kdoctor.io_netdnses.yaml +++ b/charts/crds/kdoctor.io_netdnses.yaml @@ -1132,12 +1132,10 @@ spec: type: string durationInSecond: default: 2 - format: int64 minimum: 1 type: integer perRequestTimeoutInMS: default: 5 - format: int64 minimum: 1 type: integer protocol: @@ -1149,7 +1147,6 @@ spec: type: string qps: default: 5 - format: int64 minimum: 1 type: integer type: object diff --git a/pkg/k8s/apis/kdoctor.io/v1beta1/netdns_types.go b/pkg/k8s/apis/kdoctor.io/v1beta1/netdns_types.go index 85768691..0af60e45 100644 --- a/pkg/k8s/apis/kdoctor.io/v1beta1/netdns_types.go +++ b/pkg/k8s/apis/kdoctor.io/v1beta1/netdns_types.go @@ -68,17 +68,17 @@ type NetdnsRequest struct { // +kubebuilder:validation:Optional // +kubebuilder:default=2 // +kubebuilder:validation:Minimum=1 - DurationInSecond *uint64 `json:"durationInSecond,omitempty"` + DurationInSecond int `json:"durationInSecond,omitempty"` // +kubebuilder:validation:Optional // +kubebuilder:default=5 // +kubebuilder:validation:Minimum=1 - QPS *uint64 `json:"qps,omitempty"` + QPS int `json:"qps,omitempty"` // +kubebuilder:validation:Optional // +kubebuilder:default=5 // +kubebuilder:validation:Minimum=1 - PerRequestTimeoutInMS *uint64 `json:"perRequestTimeoutInMS,omitempty"` + PerRequestTimeoutInMS int `json:"perRequestTimeoutInMS,omitempty"` // +kubebuilder:default=kubernetes.default.svc.cluster.local // +kubebuilder:validation:Optional diff --git a/pkg/k8s/apis/kdoctor.io/v1beta1/zz_generated.deepcopy.go b/pkg/k8s/apis/kdoctor.io/v1beta1/zz_generated.deepcopy.go index aff34521..7011e647 100644 --- a/pkg/k8s/apis/kdoctor.io/v1beta1/zz_generated.deepcopy.go +++ b/pkg/k8s/apis/kdoctor.io/v1beta1/zz_generated.deepcopy.go @@ -545,21 +545,6 @@ func (in *NetdnsList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetdnsRequest) DeepCopyInto(out *NetdnsRequest) { *out = *in - if in.DurationInSecond != nil { - in, out := &in.DurationInSecond, &out.DurationInSecond - *out = new(uint64) - **out = **in - } - if in.QPS != nil { - in, out := &in.QPS, &out.QPS - *out = new(uint64) - **out = **in - } - if in.PerRequestTimeoutInMS != nil { - in, out := &in.PerRequestTimeoutInMS, &out.PerRequestTimeoutInMS - *out = new(uint64) - **out = **in - } if in.Protocol != nil { in, out := &in.Protocol, &out.Protocol *out = new(string) diff --git a/pkg/k8s/apis/system/v1beta1/apphttphealthy.go b/pkg/k8s/apis/system/v1beta1/apphttphealthy.go index 9718039a..404629e4 100644 --- a/pkg/k8s/apis/system/v1beta1/apphttphealthy.go +++ b/pkg/k8s/apis/system/v1beta1/apphttphealthy.go @@ -10,13 +10,13 @@ import ( const AppHttpHealthyTaskName = "AppHttpHealthy" type AppHttpHealthyTask struct { - TargetType string `json:"TargetType"` - TargetNumber int64 `json:"TargetNumber"` - FailureReason *string `json:"FailureReason,omitempty"` - Succeed bool `json:"Succeed"` - MaxCPU string `json:"MaxCPU"` - MaxMemory string `json:"MaxMemory"` - Detail []AppHttpHealthyTaskDetail `json:"Detail"` + TargetType string `json:"TargetType"` + TargetNumber int64 `json:"TargetNumber"` + FailureReason *string `json:"FailureReason,omitempty"` + Succeed bool `json:"Succeed"` + SystemResource SystemResource `json:"SystemResource"` + TotalRunningLoad TotalRunningLoad `json:"TotalRunningLoad"` + Detail []AppHttpHealthyTaskDetail `json:"Detail"` } type AppHttpHealthyTaskDetail struct { diff --git a/pkg/k8s/apis/system/v1beta1/common_types.go b/pkg/k8s/apis/system/v1beta1/common_types.go index 35b16c6c..79936ebb 100644 --- a/pkg/k8s/apis/system/v1beta1/common_types.go +++ b/pkg/k8s/apis/system/v1beta1/common_types.go @@ -19,3 +19,14 @@ type LatencyDistribution struct { // Mean is the mean request latency. Mean float32 `json:"Mean_inMs"` } + +type TotalRunningLoad struct { + AppHttpHealthyQPS int64 `json:"AppHttpHealthyQPS"` + NetReachQPS int64 `json:"NetReachQPS"` + NetDnsQPS int64 `json:"NetDnsQPS"` +} + +type SystemResource struct { + MaxCPU string `json:"MaxCPU"` + MaxMemory string `json:"MaxMemory"` +} diff --git a/pkg/k8s/apis/system/v1beta1/netdns.go b/pkg/k8s/apis/system/v1beta1/netdns.go index f6e6351f..ec56afe4 100644 --- a/pkg/k8s/apis/system/v1beta1/netdns.go +++ b/pkg/k8s/apis/system/v1beta1/netdns.go @@ -10,13 +10,13 @@ import ( const NetDNSTaskName = "Netdns" type NetDNSTask struct { - TargetType string `json:"targetType"` - TargetNumber int64 `json:"targetNumber"` - FailureReason *string `json:"failureReason,omitempty"` - Succeed bool `json:"succeed"` - MaxCPU string `json:"MaxCPU"` - MaxMemory string `json:"MaxMemory"` - Detail []NetDNSTaskDetail `json:"detail"` + TargetType string `json:"targetType"` + TargetNumber int64 `json:"targetNumber"` + FailureReason *string `json:"failureReason,omitempty"` + Succeed bool `json:"succeed"` + SystemResource SystemResource `json:"SystemResource"` + TotalRunningLoad TotalRunningLoad `json:"TotalRunningLoad"` + Detail []NetDNSTaskDetail `json:"detail"` } type NetDNSTaskDetail struct { diff --git a/pkg/k8s/apis/system/v1beta1/netreach.go b/pkg/k8s/apis/system/v1beta1/netreach.go index 6939030d..79e5d89d 100644 --- a/pkg/k8s/apis/system/v1beta1/netreach.go +++ b/pkg/k8s/apis/system/v1beta1/netreach.go @@ -6,13 +6,13 @@ package v1beta1 const NetReachTaskName = "NetReach" type NetReachTask struct { - TargetType string `json:"TargetType"` - TargetNumber int64 `json:"TargetNumber"` - FailureReason *string `json:"FailureReason,omitempty"` - Succeed bool `json:"Succeed"` - MaxCPU string `json:"MaxCPU"` - MaxMemory string `json:"MaxMemory"` - Detail []NetReachTaskDetail `json:"Detail"` + TargetType string `json:"TargetType"` + TargetNumber int64 `json:"TargetNumber"` + FailureReason *string `json:"FailureReason,omitempty"` + Succeed bool `json:"Succeed"` + SystemResource SystemResource `json:"SystemResource"` + TotalRunningLoad TotalRunningLoad `json:"TotalRunningLoad"` + Detail []NetReachTaskDetail `json:"Detail"` } type NetReachTaskDetail struct { diff --git a/pkg/k8s/apis/system/v1beta1/zz_generated.deepcopy.go b/pkg/k8s/apis/system/v1beta1/zz_generated.deepcopy.go index 79021d17..87d8c53c 100644 --- a/pkg/k8s/apis/system/v1beta1/zz_generated.deepcopy.go +++ b/pkg/k8s/apis/system/v1beta1/zz_generated.deepcopy.go @@ -18,6 +18,8 @@ func (in *AppHttpHealthyTask) DeepCopyInto(out *AppHttpHealthyTask) { *out = new(string) **out = **in } + out.SystemResource = in.SystemResource + out.TotalRunningLoad = in.TotalRunningLoad if in.Detail != nil { in, out := &in.Detail, &out.Detail *out = make([]AppHttpHealthyTaskDetail, len(*in)) @@ -234,6 +236,8 @@ func (in *NetDNSTask) DeepCopyInto(out *NetDNSTask) { *out = new(string) **out = **in } + out.SystemResource = in.SystemResource + out.TotalRunningLoad = in.TotalRunningLoad if in.Detail != nil { in, out := &in.Detail, &out.Detail *out = make([]NetDNSTaskDetail, len(*in)) @@ -282,6 +286,8 @@ func (in *NetReachTask) DeepCopyInto(out *NetReachTask) { *out = new(string) **out = **in } + out.SystemResource = in.SystemResource + out.TotalRunningLoad = in.TotalRunningLoad if in.Detail != nil { in, out := &in.Detail, &out.Detail *out = make([]NetReachTaskDetail, len(*in)) @@ -373,3 +379,33 @@ func (in *Report) DeepCopy() *Report { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SystemResource) DeepCopyInto(out *SystemResource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SystemResource. +func (in *SystemResource) DeepCopy() *SystemResource { + if in == nil { + return nil + } + out := new(SystemResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TotalRunningLoad) DeepCopyInto(out *TotalRunningLoad) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TotalRunningLoad. +func (in *TotalRunningLoad) DeepCopy() *TotalRunningLoad { + if in == nil { + return nil + } + out := new(TotalRunningLoad) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/pluginManager/agentManager.go b/pkg/pluginManager/agentManager.go index 4ae970f3..11de9142 100644 --- a/pkg/pluginManager/agentManager.go +++ b/pkg/pluginManager/agentManager.go @@ -6,6 +6,7 @@ package pluginManager import ( "context" "fmt" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" networkingv1 "k8s.io/api/networking/v1" "time" @@ -85,6 +86,8 @@ func (s *pluginManager) RunAgentController() { logger.Sugar().Fatalf("unsupported TaskKind %s in %v", types.AgentConfig.TaskKind, types.TaskKinds) } + runningTaskManager := runningTask.InitRunningTask() + for name, plugin := range s.chainingPlugins { if name != types.AgentConfig.TaskKind && !types.AgentConfig.DefaultAgent { continue @@ -92,13 +95,14 @@ func (s *pluginManager) RunAgentController() { logger.Sugar().Infof("run controller for plugin %v", name) k := &pluginAgentReconciler{ - logger: logger.Named(name + "Reconciler"), - plugin: plugin, - client: mgr.GetClient(), - crdKind: name, - taskRoundData: taskStatusManager.NewTaskStatus(), - localNodeName: types.AgentConfig.LocalNodeName, - fm: fm, + logger: logger.Named(name + "Reconciler"), + plugin: plugin, + client: mgr.GetClient(), + crdKind: name, + taskRoundData: taskStatusManager.NewTaskStatus(), + localNodeName: types.AgentConfig.LocalNodeName, + fm: fm, + runningTaskManager: runningTaskManager, } if e := k.SetupWithManager(mgr); e != nil { s.logger.Sugar().Fatalf("failed to builder reconcile for plugin %v, error=%v", name, e) diff --git a/pkg/pluginManager/agentReconciler.go b/pkg/pluginManager/agentReconciler.go index 2a066b07..83935ffe 100644 --- a/pkg/pluginManager/agentReconciler.go +++ b/pkg/pluginManager/agentReconciler.go @@ -5,6 +5,7 @@ package pluginManager import ( "context" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" "reflect" "go.uber.org/zap" @@ -20,13 +21,14 @@ import ( ) type pluginAgentReconciler struct { - client client.Client - plugin plugintypes.ChainingPlugin - logger *zap.Logger - crdKind string - localNodeName string - taskRoundData taskStatusManager.TaskStatus - fm fileManager.FileManager + client client.Client + plugin plugintypes.ChainingPlugin + logger *zap.Logger + crdKind string + localNodeName string + taskRoundData taskStatusManager.TaskStatus + fm fileManager.FileManager + runningTaskManager *runningTask.RunningTask } var _ reconcile.Reconciler = &pluginAgentReconciler{} diff --git a/pkg/pluginManager/agentTools.go b/pkg/pluginManager/agentTools.go index ef0815bd..ec916955 100644 --- a/pkg/pluginManager/agentTools.go +++ b/pkg/pluginManager/agentTools.go @@ -8,10 +8,10 @@ import ( "context" "encoding/json" "fmt" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" "strings" "time" - "github.com/kdoctor-io/kdoctor/pkg/resource" "go.uber.org/zap" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -34,11 +34,63 @@ func (s *pluginAgentReconciler) CallPluginImplementRoundTask(logger *zap.Logger, defer cancel() taskSucceed := make(chan bool) logger.Sugar().Infof("plugin begins to implement, expect deadline %v, ", roundDuration.String()) + // get task qps + var qps int + switch s.crdKind { + case KindNameAppHttpHealthy: + app := obj.(*crd.AppHttpHealthy) + qps = app.Spec.Request.QPS + case KindNameNetReach: + app := obj.(*crd.NetReach) + caseNum := 0 + // Multiple use cases of netreach are executed simultaneously, so qps needs to be multiplied by the number of use cases + if *app.Spec.Target.ClusterIP { + if *app.Spec.Target.IPv4 { + caseNum += 1 + } + if *app.Spec.Target.IPv6 { + caseNum += 1 + } + } + if *app.Spec.Target.LoadBalancer { + if *app.Spec.Target.IPv4 { + caseNum += 1 + } + if *app.Spec.Target.IPv6 { + caseNum += 1 + } + } + if *app.Spec.Target.Endpoint { + if *app.Spec.Target.IPv4 { + caseNum += 1 + } + if *app.Spec.Target.IPv6 { + caseNum += 1 + } + } + if *app.Spec.Target.NodePort { + if *app.Spec.Target.IPv4 { + caseNum += 1 + } + if *app.Spec.Target.IPv6 { + caseNum += 1 + } + } + if *app.Spec.Target.Ingress { + if *app.Spec.Target.IPv4 { + caseNum += 1 + } + } + qps = app.Spec.Request.QPS * caseNum + case KindNameNetdns: + app := obj.(*crd.Netdns) + qps = app.Spec.Request.QPS + } + beforeQPS := s.runningTaskManager.QpsStats() + logger.Sugar().Debugf("Before the current task starts, the total qps of the tasks being executed is AppHttpHealth=%d,NetReach=%d,NetDNS=%d", beforeQPS.AppHttpHealthyQPS, beforeQPS.NetReachQPS, beforeQPS.NetDnsQPS) + s.runningTaskManager.SetTask(runningTask.Task{Name: taskName, Kind: s.crdKind, Qps: qps}) go func() { - // process mem cpu stats - resourceStats := resource.InitResource(ctx) - resourceStats.RunResourceCollector() startTime := metav1.Now() msg := &systemv1beta1.Report{ TaskName: strings.ToLower(taskName), @@ -49,7 +101,7 @@ func (s *pluginAgentReconciler) CallPluginImplementRoundTask(logger *zap.Logger, StartTimeStamp: startTime, ReportType: plugintypes.ReportTypeAgent, } - failureReason, report, e := s.plugin.AgentExecuteTask(logger, ctx, obj, resourceStats) + failureReason, report, e := s.plugin.AgentExecuteTask(logger, ctx, obj, s.runningTaskManager) if e != nil { logger.Sugar().Errorf("plugin failed to implement the round task, error=%v", e) @@ -129,6 +181,8 @@ func (s *pluginAgentReconciler) CallPluginImplementRoundTask(logger *zap.Logger, } } + s.runningTaskManager.DeleteTask(taskName) + // delete data go func() { time.Sleep(roundDuration) @@ -179,7 +233,6 @@ func (s *pluginAgentReconciler) HandleAgentTaskRound(logger *zap.Logger, ctx con if status, existed := s.taskRoundData.CheckTask(taskRoundName); !existed { // mark to started it s.taskRoundData.SetTask(taskRoundName, taskStatusManager.RoundStatusOngoing) - // we still have not reported the result for an ongoing round. do it go s.CallPluginImplementRoundTask(logger.Named(taskRoundName), obj, schedulePlan, taskName, latestRecord.RoundNumber, crdObjSpec) logger.Sugar().Infof("task %v , trigger to implement task round, and try to poll report after %v second", taskRoundName, types.AgentConfig.Configmap.TaskPollIntervalInSecond) diff --git a/pkg/pluginManager/apphttphealthy/agentExecuteTask.go b/pkg/pluginManager/apphttphealthy/agentExecuteTask.go index 8424f88e..547a6b2e 100644 --- a/pkg/pluginManager/apphttphealthy/agentExecuteTask.go +++ b/pkg/pluginManager/apphttphealthy/agentExecuteTask.go @@ -15,6 +15,7 @@ import ( "github.com/kdoctor-io/kdoctor/pkg/loadRequest/loadHttp" "github.com/kdoctor-io/kdoctor/pkg/pluginManager/types" "github.com/kdoctor-io/kdoctor/pkg/resource" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/pointer" @@ -68,7 +69,11 @@ type TestTarget struct { Method loadHttp.HttpMethod } -func (s *PluginAppHttpHealthy) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, r *resource.UsedResource) (finalfailureReason string, finalReport types.Task, err error) { +func (s *PluginAppHttpHealthy) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, rt *runningTask.RunningTask) (finalfailureReason string, finalReport types.Task, err error) { + // process mem cpu stats + resourceStats := resource.InitResource(ctx) + resourceStats.RunResourceCollector() + finalfailureReason = "" task := &v1beta1.AppHttpHealthyTask{} err = nil @@ -172,11 +177,9 @@ func (s *PluginAppHttpHealthy) AgentExecuteTask(logger *zap.Logger, ctx context. } else { task.Succeed = true } - mem, cpu := r.Stats() - task.MaxMemory = fmt.Sprintf("%.2fMB", float64(mem/(1024*1024))) - task.MaxCPU = fmt.Sprintf("%.3f%%", cpu) - // every round done clean cpu mem stats - r.CleanStats() + task.SystemResource = resourceStats.Stats() + resourceStats.Stop() + task.TotalRunningLoad = rt.QpsStats() return finalfailureReason, task, err diff --git a/pkg/pluginManager/netdns/agentExecuteTask.go b/pkg/pluginManager/netdns/agentExecuteTask.go index 149aa220..3217b0a4 100644 --- a/pkg/pluginManager/netdns/agentExecuteTask.go +++ b/pkg/pluginManager/netdns/agentExecuteTask.go @@ -6,6 +6,7 @@ package netdns import ( "context" "fmt" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" "net" "strconv" "sync" @@ -77,7 +78,11 @@ type testTarget struct { Request *loadDns.DnsRequestData } -func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, r *resource.UsedResource) (finalfailureReason string, finalReport types.Task, err error) { +func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, rt *runningTask.RunningTask) (finalfailureReason string, finalReport types.Task, err error) { + // process mem cpu stats + resourceStats := resource.InitResource(ctx) + resourceStats.RunResourceCollector() + finalfailureReason = "" instance, ok := obj.(*crd.Netdns) @@ -103,9 +108,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } else { @@ -114,9 +119,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeAAAA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } @@ -139,9 +144,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } else if ip.To4() == nil && *instance.Spec.Target.NetDnsTargetDns.TestIPv6 { @@ -150,9 +155,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeAAAA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } @@ -171,9 +176,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } else if ip.To4() == nil && *instance.Spec.Target.NetDnsTargetDns.TestIPv6 { @@ -182,9 +187,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsType: dns.TypeAAAA, TargetDomain: instance.Spec.Request.Domain, DnsServerAddr: server, - PerRequestTimeoutInMs: int(*instance.Spec.Request.PerRequestTimeoutInMS), - Qps: int(*instance.Spec.Request.QPS), - DurationInSecond: int(*instance.Spec.Request.DurationInSecond), + PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, + Qps: instance.Spec.Request.QPS, + DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) } @@ -227,11 +232,9 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, } else { task.Succeed = true } - mem, cpu := r.Stats() - task.MaxMemory = fmt.Sprintf("%.2fMB", float64(mem/(1024*1024))) - task.MaxCPU = fmt.Sprintf("%.3f%%", cpu) - // every round done clean cpu mem stats - r.CleanStats() + task.SystemResource = resourceStats.Stats() + resourceStats.Stop() + task.TotalRunningLoad = rt.QpsStats() return finalfailureReason, task, err } diff --git a/pkg/pluginManager/netdns/webhook.go b/pkg/pluginManager/netdns/webhook.go index 80a65fa9..d5a8b9a0 100644 --- a/pkg/pluginManager/netdns/webhook.go +++ b/pkg/pluginManager/netdns/webhook.go @@ -62,17 +62,17 @@ func (s *PluginNetDns) WebhookValidateCreate(logger *zap.Logger, ctx context.Con // validate request if true { - if int(*r.Spec.Request.QPS) >= types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps { + if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps { s := fmt.Sprintf("netdns %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps) logger.Error(s) return apierrors.NewBadRequest(s) } - if int(*r.Spec.Request.PerRequestTimeoutInMS) > int(r.Spec.Schedule.RoundTimeoutMinute*60*1000) { + if r.Spec.Request.PerRequestTimeoutInMS > int(r.Spec.Schedule.RoundTimeoutMinute*60*1000) { s := fmt.Sprintf("netdns %v requires PerRequestTimeoutInMS %v ms smaller than Schedule.RoundTimeoutMinute %vm ", r.Name, r.Spec.Request.PerRequestTimeoutInMS, r.Spec.Schedule.RoundTimeoutMinute) logger.Error(s) return apierrors.NewBadRequest(s) } - if int(*r.Spec.Request.DurationInSecond) > int(r.Spec.Schedule.RoundTimeoutMinute*60) { + if r.Spec.Request.DurationInSecond > int(r.Spec.Schedule.RoundTimeoutMinute*60) { s := fmt.Sprintf("netdns %v requires request.DurationInSecond %vs smaller than Schedule.RoundTimeoutMinute %vm ", r.Name, r.Spec.Request.DurationInSecond, r.Spec.Schedule.RoundTimeoutMinute) logger.Error(s) return apierrors.NewBadRequest(s) diff --git a/pkg/pluginManager/netreach/agentExecuteTask.go b/pkg/pluginManager/netreach/agentExecuteTask.go index 80a93abc..0586e6fa 100644 --- a/pkg/pluginManager/netreach/agentExecuteTask.go +++ b/pkg/pluginManager/netreach/agentExecuteTask.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "github.com/kdoctor-io/kdoctor/pkg/resource" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" networkingv1 "k8s.io/api/networking/v1" "sync" @@ -71,7 +72,11 @@ type TestTarget struct { Method loadHttp.HttpMethod } -func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, r *resource.UsedResource) (finalfailureReason string, finalReport types.Task, err error) { +func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, rt *runningTask.RunningTask) (finalfailureReason string, finalReport types.Task, err error) { + // process mem cpu stats + resourceStats := resource.InitResource(ctx) + resourceStats.RunResourceCollector() + finalfailureReason = "" err = nil var e error @@ -321,11 +326,9 @@ func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Contex task.Succeed = true } - mem, cpu := r.Stats() - task.MaxMemory = fmt.Sprintf("%.2fMB", float64(mem/(1024*1024))) - task.MaxCPU = fmt.Sprintf("%.3f%%", cpu) - // every round done clean cpu mem stats - r.CleanStats() + task.SystemResource = resourceStats.Stats() + resourceStats.Stop() + task.TotalRunningLoad = rt.QpsStats() return finalfailureReason, task, err } diff --git a/pkg/pluginManager/types/types.go b/pkg/pluginManager/types/types.go index b9c4505d..5a2def9e 100644 --- a/pkg/pluginManager/types/types.go +++ b/pkg/pluginManager/types/types.go @@ -6,7 +6,7 @@ package types import ( "context" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" - "github.com/kdoctor-io/kdoctor/pkg/resource" + "github.com/kdoctor-io/kdoctor/pkg/runningTask" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" @@ -21,7 +21,7 @@ type Task interface { type ChainingPlugin interface { GetApiType() client.Object - AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, r *resource.UsedResource) (failureReason string, task Task, err error) + AgentExecuteTask(logger *zap.Logger, ctx context.Context, obj runtime.Object, rt *runningTask.RunningTask) (failureReason string, task Task, err error) SetReportWithTask(report *v1beta1.Report, crdSpec interface{}, task Task) error // ControllerReconcile(*zap.Logger, client.Client, context.Context, reconcile.Request) (reconcile.Result, error) diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 0d2c7cf1..96520857 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -5,6 +5,8 @@ package resource import ( "context" + "fmt" + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" "runtime" "time" @@ -15,51 +17,65 @@ import ( ) type UsedResource struct { - mem uint64 // byte - cpu float64 // percent - l lock.RWMutex - ctx context.Context + mem uint64 // byte + cpu float64 // percent + l lock.RWMutex + ctx context.Context + stop chan struct{} } func InitResource(ctx context.Context) *UsedResource { return &UsedResource{ - ctx: ctx, + ctx: ctx, + l: lock.RWMutex{}, + stop: make(chan struct{}, 1), } } func (r *UsedResource) RunResourceCollector() { interval := time.Duration(types.AgentConfig.CollectResourceInSecond) * time.Second go func() { + for { - if r.ctx.Err() != nil { + select { + case <-r.stop: return - } - cpuStats, err := cpu.Percent(interval, false) - if err == nil { - if r.cpu < cpuStats[0] { - r.cpu = cpuStats[0] + default: + if r.ctx.Err() != nil { + return } - } - m := &runtime.MemStats{} - runtime.ReadMemStats(m) - if r.mem < m.Sys { - r.mem = m.Sys + cpuStats, err := cpu.Percent(interval, false) + if err == nil { + r.l.Lock() + if r.cpu < cpuStats[0] { + r.cpu = cpuStats[0] + } + r.l.Unlock() + } + m := &runtime.MemStats{} + runtime.ReadMemStats(m) + r.l.Lock() + if r.mem < m.Sys { + r.mem = m.Sys + } + r.l.Unlock() } } - }() -} -func (r *UsedResource) Stats() (uint64, float64) { - r.l.RLock() - defer r.l.RUnlock() - m := r.mem - c := r.cpu - return m, c + }() } -func (r *UsedResource) CleanStats() { +func (r *UsedResource) Stats() v1beta1.SystemResource { r.l.Lock() defer r.l.Unlock() - r.mem = 0 - r.cpu = 0 + resource := v1beta1.SystemResource{ + MaxCPU: fmt.Sprintf("%.3f%%", r.cpu), + MaxMemory: fmt.Sprintf("%.2fMB", float64(r.mem/(1024*1024))), + } + + return resource +} + +func (r *UsedResource) Stop() { + r.stop <- struct{}{} } diff --git a/pkg/runningTask/runningTask.go b/pkg/runningTask/runningTask.go new file mode 100644 index 00000000..3b8db140 --- /dev/null +++ b/pkg/runningTask/runningTask.go @@ -0,0 +1,65 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package runningTask + +import ( + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/lock" + "github.com/kdoctor-io/kdoctor/pkg/types" +) + +type Task struct { + Kind string + Qps int + Name string +} + +type RunningTask struct { + task map[string]Task + lock.RWMutex +} + +func InitRunningTask() *RunningTask { + return &RunningTask{ + task: make(map[string]Task, 0), + } +} + +func (rt *RunningTask) SetTask(task Task) { + rt.Lock() + defer rt.Unlock() + rt.task[task.Name] = task +} + +func (rt *RunningTask) DeleteTask(taskName string) { + rt.Lock() + defer rt.Unlock() + delete(rt.task, taskName) +} + +func (rt *RunningTask) QpsStats() v1beta1.TotalRunningLoad { + rt.Lock() + defer rt.Unlock() + + var appHealthQps int + var netDNSQps int + var netReachQps int + + for _, v := range rt.task { + switch v.Kind { + case types.KindNameAppHttpHealthy: + appHealthQps += v.Qps + case types.KindNameNetReach: + netReachQps += v.Qps + case types.KindNameNetdns: + netDNSQps += v.Qps + } + } + + return v1beta1.TotalRunningLoad{ + AppHttpHealthyQPS: int64(appHealthQps), + NetDnsQPS: int64(netDNSQps), + NetReachQPS: int64(netReachQps), + } +} diff --git a/test/docs/AppHttpHealth.md b/test/docs/AppHttpHealth.md index 7582765e..80a76c51 100644 --- a/test/docs/AppHttpHealth.md +++ b/test/docs/AppHttpHealth.md @@ -16,4 +16,3 @@ | A00012 | Successfully http testing appHttpHealth due to success rate case | p1 | | done | | | A00013 | Successfully https testing appHttpHealth method GET Protocol Http2 case | p1 | | done | | | A00014 | Report request count equal real request count case | p1 | | done | | -| A00015 | Failed http every round testing appHttpHealth due to delay | p1 | | done | | diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index dd704d1c..93fdbd20 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -779,68 +779,6 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Failed http every round testing appHttpHealth due to delay ", Label("A00015"), func() { - var e error - successRate := float64(1) - successMean := int64(200) - crontab := "0 1" - appHttpHealthName := "apphttphealth-get" + tools.RandomName() - - appHttpHealth := new(v1beta1.AppHttpHealthy) - appHttpHealth.Name = appHttpHealthName - - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - - // successCondition - successCondition := new(v1beta1.NetSuccessCondition) - successCondition.SuccessRate = &successRate - successCondition.MeanAccessDelayInMs = &successMean - appHttpHealth.Spec.SuccessCondition = successCondition - - // target - target := new(v1beta1.AppHttpHealthyTarget) - target.Method = "GET" - if net.ParseIP(testSvcIP).To4() == nil { - target.Host = fmt.Sprintf("http://[%s]:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) - } else { - target.Host = fmt.Sprintf("http://%s:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) - } - appHttpHealth.Spec.Target = target - - // request - request := new(v1beta1.NetHttpRequest) - request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 10 - request.DurationInSecond = 60 - appHttpHealth.Spec.Request = request - - // Schedule - Schedule := new(v1beta1.SchedulePlan) - Schedule.Schedule = &crontab - Schedule.RoundNumber = 1 - Schedule.RoundTimeoutMinute = 1 - appHttpHealth.Spec.Schedule = Schedule - - e = frame.CreateResource(appHttpHealth) - Expect(e).NotTo(HaveOccurred(), "create appHttpHealth resource") - - e = common.CheckRuntime(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 60) - Expect(e).NotTo(HaveOccurred(), "check task runtime spec") - - e = common.WaitKdoctorTaskDone(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") - - success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) - Expect(e).NotTo(HaveOccurred(), "compare report and task") - Expect(success).To(BeFalse(), "compare report and task result") - - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") - }) - It("Successfully testing using default daemonSet as workload with Task AppHttpHealthy ", Label("E00014"), func() { var e error successRate := float64(1) diff --git a/test/e2e/common/tools.go b/test/e2e/common/tools.go index a69719c4..a31e037f 100644 --- a/test/e2e/common/tools.go +++ b/test/e2e/common/tools.go @@ -16,16 +16,16 @@ import ( "strings" "time" - "k8s.io/apimachinery/pkg/api/errors" - "github.com/docker/docker/api/types" docker_client "github.com/docker/docker/client" "github.com/onsi/ginkgo/v2" frame "github.com/spidernet-io/e2eframework/framework" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" "github.com/kdoctor-io/kdoctor/api/v1/agentServer/models" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" @@ -170,7 +170,6 @@ func GetPluginReportResult(f *frame.Framework, name string, n int) (*kdoctor_rep if err != nil { return nil, fmt.Errorf("read plugin report body failed,err : %v", err) } - ginkgo.GinkgoWriter.Println(string(body)) report := new(kdoctor_report.KdoctorReport) err = json.Unmarshal(body, report) @@ -202,6 +201,10 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n break } } + + out, _ := yaml.Marshal(r) + ginkgo.GinkgoWriter.Println(string(out)) + switch taskKind { case pluginManager.KindNameNetReach: obj := object.(*v1beta1.NetReach) @@ -359,7 +362,7 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n for _, v := range *r.Spec.Report { for _, m := range v.NetDNSTask.Detail { // qps - expectCount := float64((*rs.Spec.Request.QPS) * (*rs.Spec.Request.DurationInSecond)) + expectCount := float64((rs.Spec.Request.QPS) * (rs.Spec.Request.DurationInSecond)) realCount := float64(m.Metrics.RequestCounts) // report request count reportRequestCount += m.Metrics.RequestCounts diff --git a/test/e2e/netdns/netdns_test.go b/test/e2e/netdns/netdns_test.go index 1830404a..7820d2ec 100644 --- a/test/e2e/netdns/netdns_test.go +++ b/test/e2e/netdns/netdns_test.go @@ -50,12 +50,9 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(10) - var durationInSecond = uint64(10) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "udp" request.Protocol = &protocol @@ -118,12 +115,9 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(10) - var durationInSecond = uint64(10) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "udp" request.Protocol = &protocol @@ -181,12 +175,9 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(5) - var durationInSecond = uint64(5) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 5 + request.DurationInSecond = 5 request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "udp" request.Protocol = &protocol @@ -242,12 +233,9 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(5) - var durationInSecond = uint64(5) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 5 + request.DurationInSecond = 5 request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "udp" request.Protocol = &protocol @@ -307,12 +295,9 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(10) - var durationInSecond = uint64(10) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "tcp" request.Protocol = &protocol diff --git a/test/e2e/runtime/runtime_test.go b/test/e2e/runtime/runtime_test.go index cf2e690e..604cec8f 100644 --- a/test/e2e/runtime/runtime_test.go +++ b/test/e2e/runtime/runtime_test.go @@ -197,12 +197,9 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(10) - var durationInSecond = uint64(10) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 request.Domain = "www.baidu.com" protocol := "udp" request.Protocol = &protocol @@ -423,12 +420,9 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { // request request := new(v1beta1.NetdnsRequest) - var perRequestTimeoutInMS = uint64(1000) - var qps = uint64(10) - var durationInSecond = uint64(10) - request.PerRequestTimeoutInMS = &perRequestTimeoutInMS - request.QPS = &qps - request.DurationInSecond = &durationInSecond + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 request.Domain = "www.baidu.com" protocol := "udp" request.Protocol = &protocol diff --git a/test/scripts/debugCluster.sh b/test/scripts/debugCluster.sh index 7b5eac20..8eb89715 100755 --- a/test/scripts/debugCluster.sh +++ b/test/scripts/debugCluster.sh @@ -21,11 +21,13 @@ echo "$CURRENT_FILENAME : E2E_KUBECONFIG $E2E_KUBECONFIG " COMPONENT_GOROUTINE_MAX=400 COMPONENT_PS_PROCESS_MAX=50 CONTROLLER_LABEL="app.kubernetes.io/component=kdoctor-controller" +AGENT_LABEL="app.kubernetes.io/component=kdoctor-agent" CONTROLLER_POD_LIST=$( kubectl get pods --no-headers --kubeconfig ${E2E_KUBECONFIG} --namespace ${COMPONENT_NAMESPACE} --selector ${CONTROLLER_LABEL} --output jsonpath={.items[*].metadata.name} ) +AGENT_POD_LIST=$( kubectl get pods --no-headers --kubeconfig ${E2E_KUBECONFIG} --namespace ${COMPONENT_NAMESPACE} --selector ${AGENT_LABEL} --output jsonpath={.items[*].metadata.name} ) [ -z "$CONTROLLER_POD_LIST" ] && echo "error, failed to find any kdoctor controller pod" && exit 1 - +[ -z "$AGENT_POD_LIST" ] && echo "error! failed to find any kdoctor agent pod" && exit 1 if [ -n "$E2E_LOG_FILE_NAME" ] ; then From c3209d9a9b4f1136d6bc5e860f599ae6fc791d85 Mon Sep 17 00:00:00 2001 From: ii2day Date: Thu, 2 Nov 2023 17:37:03 +0800 Subject: [PATCH 04/41] fix dns client date race Signed-off-by: ii2day --- pkg/loadRequest/loadDns/dns_requester.go | 38 +++++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index 4afd2d90..8f7a8c4e 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -25,12 +25,14 @@ package loadDns import ( + "context" "crypto/tls" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" "github.com/kdoctor-io/kdoctor/pkg/utils/stats" "github.com/miekg/dns" "go.uber.org/zap" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "net" "sync" "time" ) @@ -142,12 +144,22 @@ func (b *Work) Finish() { b.report.finalize(total) } -func (b *Work) makeRequest(client *dns.Client, conn *dns.Conn, wg *sync.WaitGroup) { +func (b *Work) makeRequest(conn *dns.Conn, wg *sync.WaitGroup) { defer wg.Done() var msg *dns.Msg var rtt time.Duration var err error + client := new(dns.Client) + client.Net = b.Protocol + client.Timeout = time.Duration(b.Timeout) * time.Millisecond + if b.Protocol == "tcp-tls" { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + client.TLSConfig = tlsConfig + } + if b.Protocol == "tcp" || b.Protocol == "tcp-tls" { msg, rtt, err = client.Exchange(b.Msg, b.ServerAddr) @@ -166,15 +178,10 @@ func (b *Work) makeRequest(client *dns.Client, conn *dns.Conn, wg *sync.WaitGrou } func (b *Work) runWorker() { - client := new(dns.Client) - client.Net = b.Protocol - client.Timeout = time.Duration(b.Timeout) * time.Millisecond - conn, _ := client.Dial(b.ServerAddr) - if b.Protocol == "tcp-tls" { - tlsConfig := &tls.Config{ - InsecureSkipVerify: true, - } - client.TLSConfig = tlsConfig + conn, err := b.makeConn() + if err != nil { + b.Logger.Sugar().Errorf("failed create dns conn,err=%v", err) + return } wg := &sync.WaitGroup{} for { @@ -185,7 +192,7 @@ func (b *Work) runWorker() { return case <-b.qosTokenBucket: wg.Add(1) - go b.makeRequest(client, conn, wg) + go b.makeRequest(conn, wg) } } } @@ -250,3 +257,12 @@ func (b *Work) AggregateMetric() *v1beta1.DNSMetrics { return metric } + +func (b *Work) makeConn() (*dns.Conn, error) { + var err error + d := net.Dialer{Timeout: time.Duration(b.Timeout) * time.Millisecond} + conn := new(dns.Conn) + conn.Conn, err = d.DialContext(context.Background(), "udp", b.ServerAddr) + + return conn, err +} From f9edbce4b32518991c100a991078a421bd2398b5 Mon Sep 17 00:00:00 2001 From: ii2day Date: Mon, 6 Nov 2023 11:16:08 +0800 Subject: [PATCH 05/41] report resource add mean cpu Signed-off-by: ii2day --- pkg/k8s/apis/system/v1beta1/common_types.go | 1 + pkg/resource/resource.go | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pkg/k8s/apis/system/v1beta1/common_types.go b/pkg/k8s/apis/system/v1beta1/common_types.go index 79936ebb..c2635d51 100644 --- a/pkg/k8s/apis/system/v1beta1/common_types.go +++ b/pkg/k8s/apis/system/v1beta1/common_types.go @@ -28,5 +28,6 @@ type TotalRunningLoad struct { type SystemResource struct { MaxCPU string `json:"MaxCPU"` + MeanCPU string `json:"MeanCPU"` MaxMemory string `json:"MaxMemory"` } diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 96520857..dcefd2f5 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -17,11 +17,13 @@ import ( ) type UsedResource struct { - mem uint64 // byte - cpu float64 // percent - l lock.RWMutex - ctx context.Context - stop chan struct{} + mem uint64 // byte + cpu float64 // percent + roundCount int // Count the number of rounds of cpu mem + totalCPU float64 //Total cpu usage statistics + l lock.RWMutex + ctx context.Context + stop chan struct{} } func InitResource(ctx context.Context) *UsedResource { @@ -35,7 +37,6 @@ func InitResource(ctx context.Context) *UsedResource { func (r *UsedResource) RunResourceCollector() { interval := time.Duration(types.AgentConfig.CollectResourceInSecond) * time.Second go func() { - for { select { case <-r.stop: @@ -47,9 +48,11 @@ func (r *UsedResource) RunResourceCollector() { cpuStats, err := cpu.Percent(interval, false) if err == nil { r.l.Lock() + r.roundCount++ if r.cpu < cpuStats[0] { r.cpu = cpuStats[0] } + r.totalCPU += cpuStats[0] r.l.Unlock() } m := &runtime.MemStats{} @@ -70,6 +73,7 @@ func (r *UsedResource) Stats() v1beta1.SystemResource { defer r.l.Unlock() resource := v1beta1.SystemResource{ MaxCPU: fmt.Sprintf("%.3f%%", r.cpu), + MeanCPU: fmt.Sprintf("%.3f%%", r.totalCPU/float64(r.roundCount)), MaxMemory: fmt.Sprintf("%.2fMB", float64(r.mem/(1024*1024))), } From 8ea6517f48c959ca2ca2814c454c3ae436f515be Mon Sep 17 00:00:00 2001 From: wjy Date: Mon, 6 Nov 2023 11:37:37 +0800 Subject: [PATCH 06/41] [#303]fix-Failed_to_get_the_dns_service_IP_list --- pkg/agentDnsServer/appDnsServer.go | 6 ++++++ pkg/k8ObjManager/service.go | 5 ++++- pkg/types/agent_config.go | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/agentDnsServer/appDnsServer.go b/pkg/agentDnsServer/appDnsServer.go index 6237d63f..6bb4fc5a 100644 --- a/pkg/agentDnsServer/appDnsServer.go +++ b/pkg/agentDnsServer/appDnsServer.go @@ -36,6 +36,12 @@ func SetupAppDnsServer(rootLogger *zap.Logger, tlsCert, tlsKey string) { if err != nil { logger.Sugar().Fatalf("failed get kube dns service,err: %v", err) } + + // If the ip address list is empty, the corresponding service does not exist or the selected label is incorrect + if len(dnsServiceIPs) == 0 { + logger.Sugar().Fatalf("failed get kube dns service: %v", "the corresponding service does not exist or the selected label is incorrect") + } + logger.Sugar().Infof("kube dns service %s ", dnsServiceIPs) coreDnsAddr = fmt.Sprintf("%s:53", dnsServiceIPs[0]) } diff --git a/pkg/k8ObjManager/service.go b/pkg/k8ObjManager/service.go index f3edf129..0bcf18b8 100644 --- a/pkg/k8ObjManager/service.go +++ b/pkg/k8ObjManager/service.go @@ -6,6 +6,7 @@ package k8sObjManager import ( "context" "fmt" + "github.com/kdoctor-io/kdoctor/pkg/types" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -98,9 +99,11 @@ func (nm *k8sObjManager) ListServicesDnsIP(ctx context.Context) ([]string, error serviceList := new(corev1.ServiceList) var err error var result []string + label := map[string]string{ - "k8s-app": "kube-dns", + types.AgentConfig.DnsServiceSelectLabelKey: types.AgentConfig.DnsServiceSelectLabelValue, } + ListOption := &client.ListOptions{ Namespace: "kube-system", LabelSelector: labels.SelectorFromSet(label), diff --git a/pkg/types/agent_config.go b/pkg/types/agent_config.go index bdb00307..2da9ab15 100644 --- a/pkg/types/agent_config.go +++ b/pkg/types/agent_config.go @@ -26,6 +26,8 @@ var AgentEnvMapping = []EnvMapping{ {"ENV_CLUSTER_DNS_DOMAIN", "cluster.local", &AgentConfig.ClusterDnsDomain}, {"ENV_LOCAL_NODE_IP", "", &AgentConfig.LocalNodeIP}, {"ENV_LOCAL_NODE_NAME", "", &AgentConfig.LocalNodeName}, + {"ENV_DNS_SERVICE_SELECT_LABEL_KEY", "kubernetes.io/name", &AgentConfig.DnsServiceSelectLabelKey}, + {"ENV_DNS_SERVICE_SELECT_LABEL_VALUE", "CoreDNS", &AgentConfig.DnsServiceSelectLabelValue}, } type AgentConfigStruct struct { @@ -51,6 +53,10 @@ type AgentConfigStruct struct { LocalNodeIP string LocalNodeName string + // dns selector label + DnsServiceSelectLabelKey string + DnsServiceSelectLabelValue string + EnableAggregateAgentReport bool DirPathAgentReport string CleanAgedReportInMinute int32 From ab5ce350904263e01be9e6bd13a025c4c88f0b7b Mon Sep 17 00:00:00 2001 From: ii2day Date: Mon, 6 Nov 2023 16:27:40 +0800 Subject: [PATCH 07/41] update readme.md Signed-off-by: ii2day --- README-zh_CN.md | 43 ++++++---- docs/develop/contributing.md | 148 +++++++++++++++++++++++++++++++++++ docs/images/arch.png | Bin 0 -> 246570 bytes 3 files changed, 177 insertions(+), 14 deletions(-) create mode 100644 docs/develop/contributing.md create mode 100644 docs/images/arch.png diff --git a/README-zh_CN.md b/README-zh_CN.md index 20eabdbb..14c75a90 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -10,31 +10,46 @@ **简体中文** | [**English**](./README.md) -## Introduction +## 介绍 -kdoctor 是一个 kubernetes 数据面测试项目,通过压力注入的方式,实现对集群进行功能、性能的主动式巡检。 +kdoctor 是一个基于主动式压力注入的 Kubernetes 数据面测试组件,对集群进行功能、性能的测试。通过调研和抽象了运维人员的常规运维需求,让网络、存储、应用等运维任务进行了云原生实现,基于 CRD的设计,能够对接观测性组件。 -传统的集群巡检,通过采集指标、日志、应用状态等信息来确认集群和应用的状态,实现被动式巡检。但是在一些特殊场景下,这种方式可能不能实现预期的巡检目的、时效性、集群范围,运维人员就需要采用手动方式给集群注入一些压力,进行主动式巡检,当集群规模很大、巡检频率高或巡检流程复杂时,手工方式难以持久实施。这些场景包括: +**kdoctor 主要包含以下 3 个类型的任务:** +* [AppHttpHealthy](./docs/reference/apphttphealthy-zh_CN.md): 根据任务配置对集群内外指定访问地址,使用 HTTP、HTTPS 协议进行连通性检查,支持 PUT、GET、POST 等多种请求方式。 +* [NetReach](./docs/reference/netreach-zh_CN.md): 根据任务配置对集群内 Pod IP、ClusterIP、NodePort、Loadbalancer IP、Ingress IP, 甚至是 POD 多网卡、双栈IP进行连通性巡检。 +* [NetDns](./docs/reference/netdns-zh_CN.md): 根据任务配置,对集群内外的指定 DNS Server 进行连通性检测,支持 udp、tcp、tcp-tls 协议。 -* 部署大规模集群后,希望确认所有节点间 POD 的网络连通性,避免某个节点存在网络故障,发现网络中是否存在偶发丢包问题,而通信渠道非常多,包括 pod IP、clusterIP、nodePort、loadbalancer ip、ingress ip, 甚至是 POD 多网卡、双栈IP +**kdoctor 较传统的测试组件有哪些优势:** +* 通过下发 CRD 配置巡检任务需求,使用者只需要关注巡检目标、巡检频率、发压参数以及期望巡检结果。 +* 通过读取任务配置,以 Deployment 或 DaemonSet 的方式运行发压 agent,以达到多台发压机器的效果。 +* 根据任务的 spec 配置,使用 default agent 或创建新的 agent 执行任务,以达到资源重复利用和任务资源隔离。 +* 绑定相对应的资源目标,如 ingress 、service,每一个 agent pod 根据任务配置相互访问绑定的资源,根据请求结果得出结论。 +* 发压 client 通过性能调优,大大降低了发压请求时的资源消耗。 +* 巡检报告通过日志、聚合 api 、文件落盘等方式输出。 -* 希望主动检测所有节点间上的 POD 能够正常访问 coredns 服务,希望确认 coredns 服务的资源配置和副本数量正确,其服务性能能够支持预期的最大访问量 +## 架构 -* 磁盘是易耗品,例如 etcd 等应用对磁盘性能是比较敏感的,在日常运维工作中,管理员希望周期地确认所有节点的本地磁盘是正常的,文件读写的吞吐量和延时是符合预期的 +
+ Your Image Description +
-* 给某个服务主动注入压力,它可能是镜像仓库、mysql 或者 api-server,以配合 BUG 复现,或确认服务性能 +组件构成: +* kdoctor controller: 以 Deployment 形式常驻,实施 CR 监控,任务创建,任务报告汇聚等。 +* kdoctor agent: 以 Deployment 或 DaemonSet 形式按需动态创建,任务的执行者。 -kdoctor 是一个 kubernetes 数据面测试项目,来源于生产运维过程中的实践场景,通过压力注入的方式,实现对集群进行功能、性能的主动式巡检。 kdoctor 可以应用于: +## 快速开始 -* 生产环境的部署检查、日常运维等场景,能避免了人工巡检的工作负担。 +**安装** +* [安装 kdoctor](./docs/usage/install-zh_CN.md) 或 [kind 快速开始](./docs/usage/install-zh_CN.md) -* 能应用 E2E 测试、bug 复现、混沌测试等,减少编程工作。 +**开始任务** +* [开始任务 AppHttpHealthy](./docs/usage/apphttphealthy-zh_CN.md) +* [开始任务 NetReach](./docs/usage/netreach-zh_CN.md) +* [开始任务 NetDNS](./docs/usage/netdns-zh_CN.md) -## 架构 - -## 快速开始 +## 参与开发 -## 核心功能 +可参考 [开发搭建文档](./docs/develop/contributing.md). ## License diff --git a/docs/develop/contributing.md b/docs/develop/contributing.md new file mode 100644 index 00000000..48f65fdc --- /dev/null +++ b/docs/develop/contributing.md @@ -0,0 +1,148 @@ +# Contribution + +*** + +## unitest + +run the following command to check unitest + +`make unitest_tests` + +## setup cluster and run test + +1. check required developing tools on you local host. If something missing, please run 'test/scripts/install-tools.sh' to install them + + # make -C test checkBin + pass 'kubectl' installed: GitVersion:"v1.24.4" + pass 'kind' installed: kind version 0.19.0 + pass 'helm' installed: Version:"v3.12.0" + pass 'docker' installed: Docker version 24.0.6, build ed223bc + +2. run the e2e + + # make e2e + + if your run it for the first time, it will download some images, you could set the http proxy + + # ADDR=10.6.0.1 + # export https_proxy=http://${ADDR}:7890 http_proxy=http://${ADDR}:7890 + # make e2e + + run a specified case + + # make e2e -e E2E_GINKGO_LABELS="lable1,label2" + +3. you could do it step by step with the follow + + if you are in China, it could add `-e E2E_CHINA_IMAGE_REGISTRY=true` to pull images from china image registry, add `-e HTTP_PROXY=http://${ADDR}` to get chart + + build the image + + # do some coding + + $ git add . + $ git commit -s -m 'message' + + # !!! images is built by commit sha, so make sure the commit is submit locally + $ make build_local_image + + setup the cluster + + # setup the kind cluster of dual-stack + # !!! images is tested by commit sha, so make sure the commit is submit locally + $ make e2e_init + + run the e2e test + + # run all e2e test on dual-stack cluster + $ make e2e_run + + # run all e2e test on ipv4-only cluster + $ make e2e_run -e E2E_IP_FAMILY=ipv4 + + # run all e2e test on ipv6-only cluster + $ make e2e_run -e E2E_IP_FAMILY=ipv6 + + $ ls e2ereport.json + + $ make e2e_clean + +5.clean `make e2e_clean` + +*** + +## Submit Pull Request + +A pull request will be checked by following workflow, which is required for merging. + +### Action: your PR should be signed off + +When you commit your modification, add `-s` in your commit command `git commit -s` + +### Action: check yaml files + +If this check fails, see the [yaml rule](https://yamllint.readthedocs.io/en/stable/rules.html). + +Once the issue is fixed, it could be verified on your local host by command `make lint-yaml`. + +Note: To ignore a yaml rule, you can add it into `.github/yamllint-conf.yml`. + +### Action: check golang source code + +It checks the following items against any updated golang file. + +* Mod dependency updated, golangci-lint, gofmt updated, go vet, use internal lock pkg + +* Comment `// TODO` should follow the format: `// TODO (AuthorName) ...`, which easy to trace the owner of the remaining job + +* Unitest and upload coverage to codecov + +* Each golang test file should mark ginkgo label + +### Action: check licenses + +Any golang or shell file should be licensed correctly. + +### Action: check markdown file + +### Action: lint yaml file + +If it fails, see for reasons. + +You can test it on your local machine with the command `make lint-yaml`. + +### Action: lint chart + +You can test it on your local machine with the command `make lint_chart_format`. + +### Action: lint openapi.yaml + +### Action: check code spell + +Any code spell error of golang files will be checked. + +You can check it on your local machine with the command `make lint-code-spell`. + +It could be automatically fixed on your local machine with the command `make fix-code-spell`. + +If you believe it can be ignored, edit `.github/codespell-ignorewords` and make sure all letters are lower-case. + +## Changelog + +How to automatically generate changelogs: + +1. All PRs should be labeled with "pr/release/***" and can be merged. + +2. When you add the label, the changelog will be created automatically. + + The changelog contents include: + + * New Features: it includes all PRs labeled with "pr/release/feature-new" + + * Changed Features: it includes all PRs labeled with "pr/release/feature-changed" + + * Fixes: it includes all PRs labeled with "pr/release/bug" + + * All historical commits within this version + +3. The changelog will be attached to Github RELEASE and submitted to /changelogs of branch 'github_pages'. diff --git a/docs/images/arch.png b/docs/images/arch.png new file mode 100644 index 0000000000000000000000000000000000000000..3cca211477ce6ae66058ac6b5f4aba420783e912 GIT binary patch literal 246570 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�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 literal 0 HcmV?d00001 From 98e49286f005b3e2b9bede1fe76d088eda757e97 Mon Sep 17 00:00:00 2001 From: "tao.yang" Date: Wed, 8 Nov 2023 17:51:39 +0800 Subject: [PATCH 08/41] Publish development documents through github actions. Signed-off-by: tao.yang --- .github/workflows/call-release-pages.yaml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/call-release-pages.yaml b/.github/workflows/call-release-pages.yaml index f7721c01..af061877 100644 --- a/.github/workflows/call-release-pages.yaml +++ b/.github/workflows/call-release-pages.yaml @@ -51,8 +51,25 @@ jobs: fetch-depth: 0 ref: ${{ env.REF }} + - name: Set main branch docs to dev (latest) + id: main_docs + if: ${{ env.REF == 'main' }} + run: | + pip install mkdocs==1.5.2 mike==1.1.2 mkdocs-material==9.2.8 + git config user.email "robot@example.com" + git config user.name "robot" + cp ./docs/mkdocs.yml ./ + mike deploy --rebase -b ${{ env.MERGE_BRANCH }} dev -t "dev (${{ env.REF }})" + rm -rf ./site && rm -rf ./mkdocs.yml + git checkout -f ${{ env.MERGE_BRANCH }} + rm -rf ./charts && rm -rf ./index.yaml && rm -rf ./changelogs + tar -czvf ./site.tar.gz * + ls + echo "push document version `dev` from branch ${{ env.REF }}." + - name: Extract Version id: extract + if: ${{ env.REF != 'main' }} run: | if ! grep -E "^[[:space:]]*v[0-9]+.[0-9]+.[0-9]+[[:space:]]*$" VERSION &>/dev/null ; then echo "not a release version, skip generating doc." @@ -82,7 +99,7 @@ jobs: - name: build doc site id: build_doc - if: ${{ env.SKIP_ALL_JOB != 'true' }} + if: ${{ env.SKIP_ALL_JOB != 'true' && env.REF != 'main' }} run: | git checkout ${{ env.REF }} ls From e9a5954ab7f3acec1a48833a36726c63d72b7b42 Mon Sep 17 00:00:00 2001 From: Jeanine-tw <76861242+Jeanine-tw@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:33:51 +0800 Subject: [PATCH 09/41] update docs --- docs/usage/apphttphealthy-zh_CN.md | 392 +++++++++++----------- docs/usage/apphttphealthy.md | 470 ++++++++++++++------------- docs/usage/get-started-kind-zh_CN.md | 25 +- docs/usage/install.md | 5 +- docs/usage/netdns-zh_CN.md | 321 +++++++++--------- docs/usage/netdns.md | 469 ++++++++++++-------------- docs/usage/netreach-zh_CN.md | 378 ++++++++++----------- docs/usage/netreach.md | 408 ++++++++++++----------- 8 files changed, 1240 insertions(+), 1228 deletions(-) diff --git a/docs/usage/apphttphealthy-zh_CN.md b/docs/usage/apphttphealthy-zh_CN.md index 145dbd82..4c0071c5 100644 --- a/docs/usage/apphttphealthy-zh_CN.md +++ b/docs/usage/apphttphealthy-zh_CN.md @@ -4,23 +4,22 @@ ## 介绍 -对于这种任务, kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent pod 都会向指定的目标发送 http 请求,默认并发量为 50 可覆盖多副本情况,并发量可在 kodcotr 的 configmap 中设置,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 +对于这种任务, kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源 ,每一个 agent Pod 都会向指定的 DNS server 发送 DNS 请求,默认并发量为 50 可覆盖多副本情况,并发量可在 kdoctor 的 configmap 中设置,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合 API 获取详细的报告。 -1.应用场景: +1. 应用场景: -* 测试连通性,确认指定应用能够被集群的每一个角落访问到 -* 大规模集群测试,模拟更多的 client 数量,以能够产生更大的压力,测试应用的抗压能力,模拟更多的源 ip 来产生更多的应用会话,测试应用的资源限制。 -* 给指定应用注入压力,配合灰度发布、混沌测试、bug 复现等目的 -* 测试集群外部服务,确认集群 egress 工作正常 + * 测试连通性,确认指定应用能够被集群的每一个角落访问到 + * 大规模集群测试,模拟更多的 client 数量,以能够产生更大的压力,测试应用的抗压能力,模拟更多的源 IP 来产生更多的应用会话,测试应用的资源限制。 + * 给指定应用注入压力,配合灰度发布、混沌测试、bug 复现等目的 + * 测试集群外部服务,确认集群 egress 工作正常 +2. 关于 AppHttpHealthy CRD 的更多描述,可参考[AppHttpHealthy](../reference/apphttphealthy-zh_CN.md) -2.关于 AppHttpHealthy CRD 的更多描述,可参考[AppHttpHealthy](../reference/apphttphealthy-zh_CN.md) - -3.功能列表: +3. 功能列表: -* 支持 HTTP、HTTPS、HTTP2,能够定义 header、body + * 支持 HTTP、HTTPS、HTTP2,能够定义 header、body -## 开始 +## 使用步骤 接下来将展示 `AppHttpHealthy` 的使用示例 @@ -30,7 +29,7 @@ ### 安装测试 server (选做) -kdoctor 官方仓库中包含了一个名为 server 的应用,内包含 http server,https server, dns server 可用来测试 kdoctor 功能,若存在其他测试的 server 可跳过安装。 +kdoctor 官方仓库中包含了一个名为 server 的应用,内包含 http server,https server, DNS server,可用来测试 kdoctor 功能,若存在其他测试的 server 可跳过安装。 ```shell helm repo add kdoctor https://kdoctor-io.github.io/kdoctor @@ -55,7 +54,7 @@ server ClusterIP 172.41.71.0 80/TCP,443/TCP,53/UDP,53/TCP,85 ### 创建 AppHttpHealthy -创建 http `AppHttpHealthy` ,该任务将执行一轮持续 10s 的任务,任务会向指定的 server 以 qps 为 10 的速度发送 Get 请求,并且立即执行。 +创建 http `AppHttpHealthy` ,该任务将执行一轮持续 10s 的任务,任务会向指定的 server 以 QPS 为 10 的速度发送 Get 请求,并且立即执行。 这里使用 server 的 service 地址,若有其他 server 地址 可使用其他 server 地址。 @@ -103,195 +102,194 @@ http true 1 1 succeed 0 1 ### 查看任务报告 -1.查看已有报告 - -```shell -kubectl get kdoctorreport -NAME CREATED AT -http 0001-01-01T00:00:00Z -``` - -2.查看具体任务报告 - -节点 kdoctor-control-plane 和节点 kdoctor-worker 上 agent 分别都执行一轮发压后,将 agent 报告聚合而成。 - -```shell -kubectl get kdoctorreport http -oyaml -apiVersion: system.kdoctor.io/v1beta1 -kind: KdoctorReport -metadata: - creationTimestamp: null - name: http -spec: - FailedRoundNumber: null - FinishedRoundNumber: 1 - Report: - - NodeName: kdoctor-control-plane - HttpAppHealthyTask: - Detail: - - MeanDelay: 10.317307 - Metrics: - Duration: 11.022081662s - EndTime: "2023-07-31T07:25:23Z" - Errors: {} - Latencies: - Max_inMx: 0 - Mean_inMs: 10.317307 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 - RequestCounts: 104 - StartTime: "2023-07-31T07:25:12Z" - StatusCodes: - "200": 104 - SuccessCounts: 104 - TPS: 9.435604197939574 - TotalDataSize: 40040 byte - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetName: HttpAppHealthy target - TargetUrl: http://172.41.71.0 - Succeed: true - TargetNumber: 1 - TargetType: HttpAppHealthy - MaxCPU: 30.651% - MaxMemory: 97.00MB - HttpAppHealthyTaskSpec: - ... - PodName: kdoctor-agent-fmr9m - ReportType: agent test report - RoundDuration: 11.038965547s - RoundNumber: 1 - RoundResult: succeed - StartTimeStamp: "2023-07-31T07:25:12Z" - EndTimeStamp: "2023-07-31T07:25:23Z" - TaskName: apphttphealthy.http - TaskType: AppHttpHealthy - - NodeName: kdoctor-worker - HttpAppHealthyTask: - Detail: - - MeanDelay: 10.548077 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetName: HttpAppHealthy target - TargetUrl: http://172.41.71.0 - Succeed: true - TargetNumber: 1 - TargetType: HttpAppHealthy - HttpAppHealthyTaskSpec: - ... - PodName: kdoctor-agent-s468h - ReportType: agent test report - RoundDuration: 11.034140236s - RoundNumber: 1 - RoundResult: succeed - StartTimeStamp: "2023-07-31T07:25:12Z" - EndTimeStamp: "2023-07-31T07:25:23Z" - TaskName: apphttphealthy.http - TaskType: AppHttpHealthy - ReportRoundNumber: 1 - RoundNumber: 1 - Status: Finished - TaskName: http - TaskType: AppHttpHealthy -``` - -> 注:若报告与预期结果不符合,可关注报告中的 MaxCPU和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 +1. 查看已有报告 + + ```shell + kubectl get kdoctorreport + NAME CREATED AT + http 0001-01-01T00:00:00Z + ``` + +2. 查看具体任务报告 + + 节点 kdoctor-control-plane 和节点 kdoctor-worker 上 agent 分别都执行一轮发压后,将 agent 报告聚合而成。 + + ```shell + kubectl get kdoctorreport http -oyaml + apiVersion: system.kdoctor.io/v1beta1 + kind: KdoctorReport + metadata: + creationTimestamp: null + name: http + spec: + FailedRoundNumber: null + FinishedRoundNumber: 1 + Report: + - NodeName: kdoctor-control-plane + HttpAppHealthyTask: + Detail: + - MeanDelay: 10.317307 + Metrics: + Duration: 11.022081662s + EndTime: "2023-07-31T07:25:23Z" + Errors: {} + Latencies: + Max_inMx: 0 + Mean_inMs: 10.317307 + Min_inMs: 0 + P50_inMs: 0 + P90_inMs: 0 + P95_inMs: 0 + P99_inMs: 0 + RequestCounts: 104 + StartTime: "2023-07-31T07:25:12Z" + StatusCodes: + "200": 104 + SuccessCounts: 104 + TPS: 9.435604197939574 + TotalDataSize: 40040 byte + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetName: HttpAppHealthy target + TargetUrl: http://172.41.71.0 + Succeed: true + TargetNumber: 1 + TargetType: HttpAppHealthy + MaxCPU: 30.651% + MaxMemory: 97.00MB + HttpAppHealthyTaskSpec: + ... + PodName: kdoctor-agent-fmr9m + ReportType: agent test report + RoundDuration: 11.038965547s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-07-31T07:25:12Z" + EndTimeStamp: "2023-07-31T07:25:23Z" + TaskName: apphttphealthy.http + TaskType: AppHttpHealthy + - NodeName: kdoctor-worker + HttpAppHealthyTask: + Detail: + - MeanDelay: 10.548077 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetName: HttpAppHealthy target + TargetUrl: http://172.41.71.0 + Succeed: true + TargetNumber: 1 + TargetType: HttpAppHealthy + HttpAppHealthyTaskSpec: + ... + PodName: kdoctor-agent-s468h + ReportType: agent test report + RoundDuration: 11.034140236s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-07-31T07:25:12Z" + EndTimeStamp: "2023-07-31T07:25:23Z" + TaskName: apphttphealthy.http + TaskType: AppHttpHealthy + ReportRoundNumber: 1 + RoundNumber: 1 + Status: Finished + TaskName: http + TaskType: AppHttpHealthy + ``` + +> 若报告与预期结果不符合,可关注报告中的 MaxCPU和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 ## 其他常用示例 下面是携带 body 的 http 请求示例和 https 的请求示例 -1.创建带有 body 的 http `AppHttpHealthy`,该任务将执行一轮持续 10s 的任务,任务会向指定的 server 以 qps 为 10 的速度携带body 进行 Post 请求,并且立即执行。 - -这里使用 server 的 service 地址,若有其他 server 地址 可使用其他 server 地址。 - -创建测试 body 数据 - -```shell -cat <notice: when test targetAgent case, it will send http request to all targets at the same time with spec.request.qps for each one. That meaning, the actually QPS may be bigger than spec.request.qps - -* spec.expect: define the success condition of the task result +1. Use cases: - meanAccessDelayInMs: mean access delay in MS, if the actual delay is bigger than this, it results to be failure + * Test connectivity to ensure that a specific application can be accessed from every corner of the cluster. + * Conduct large-scale cluster testing by simulating a higher number of clients to generate increased pressure and assess the application's resilience. Simulate more source IPs to create additional application sessions and test resource limitations. + * Inject pressure into a specific application for purposes such as gray release, chaos tests, bug reproduction, etc. + * Test external services of the cluster to verify the proper functioning of cluster egress. - successRate: the success rate of all http requests. Notice, when a http response code is >=200 and < 400, it's treated as success. if the actual whole success rate is smaller than successRate, the task results to be failure - - statusCode: Expect the HTTP status code returned by each request +2. For a more detailed description of the AppHttpHealthy CRD, please refer to[AppHttpHealthy](../reference/apphttphealthy.md) -* status: the status of the task +3. Features - doneRound: how many rounds have finished + * Support HTTP, HTTPS, and HTTP2 protocols, allowing customization of headers and bodies. - expectedRound: how many rounds the task expect +## Steps - finish: whether all rounds of this task have finished +The following example demonstrates how to use `AppHttpHealthy`. - lastRoundStatus: the result of last round +### Install kdoctor - history: +Follow the [installation guide](./install.md) to install kdoctor. - roundNumber: the round number +### Install Test Server (Optional) - status: the status of this round +The official kdoctor repository includes an application called "server" that contains an HTTP server, HTTPS server, and DNS server. This server can be employed to test the functionality of kdoctor. If you have other test servers available, you can skip this installation step. - startTimeStamp: when this round begins - - endTimeStamp: when this round finally finished - - duration: how long the round spent - - deadLineTimeStamp: the time deadline of a round - - failedAgentNodeList: the node list where failed kdoctor agent locate +```shell +helm repo add kdoctor https://kdoctor-io.github.io/kdoctor +helm repo update kdoctor +helm install server kdoctor/server -n kdoctor --wait --debug --create-namespace +``` - notReportAgentNodeList: the node list where uknown kdoctor agent locate. This means these agents have problems. +Check the status of test server +```shell +kubectl get pod -n kdoctor -owide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +server-7649566ff9-dv4jc 1/1 Running 0 76s 172.40.1.45 kdoctor-worker +server-7649566ff9-qc5dh 1/1 Running 0 76s 172.40.0.35 kdoctor-control-plane +``` - succeedAgentNodeList: the node list where successful kdoctor agent locate +Obtain the service address of the test server +```shell +kubectl get service -n kdoctor +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +server ClusterIP 172.41.71.0 80/TCP,443/TCP,53/UDP,53/TCP,853/TCP 2m31s +``` +### Create AppHttpHealthy -## example +Create an `AppHttpHealthy` task for HTTP that will run continuously for 10 seconds. The task will send GET requests to the specified server at a rate of 10 QPS and be executed immediately. -a quick task to test kdoctor agent, to verify the whole network is ok, each agent could reach specific host +We are using the service address of the test server. If you have a different server address available, feel free to use it instead. ```shell - -cat < test-httpapphealthy.yaml +SERVER="172.41.71.0" +cat < http-body.yaml -apiVersion: v1 -data: - body: | - {test:test} -kind: ConfigMap -metadata: - name: http-body - namespace: kube-system -EOF -kubectl apply -f http-body.yaml -``` - -### example https cert -```shell -cat < https-cert.yaml -apiVersion: v1 -data: - ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURWekNDQWorZ0F3SUJBZ0lKQU1vL3p5bGZZZzVSTUEwR0NTcUdTSWIzRFFFQkN3VUFNRUl4Q3pBSkJnTlYKQkFZVEFsaFlNUlV3RXdZRFZRUUhEQXhFWldaaGRXeDBJRU5wZEhreEhEQWFCZ05WQkFvTUUwUmxabUYxYkhRZwpRMjl0Y0dGdWVTQk1kR1F3SGhjTk1qTXdOakE0TURrd05URTVXaGNOTWpRd05qQTNNRGt3TlRFNVdqQkNNUXN3CkNRWURWUVFHRXdKWVdERVZNQk1HQTFVRUJ3d01SR1ZtWVhWc2RDQkRhWFI1TVJ3d0dnWURWUVFLREJORVpXWmgKZFd4MElFTnZiWEJoYm5rZ1RIUmtNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQQpwWERBUGt1UzhvNW9lRTBBS0ZxL2Vjb1pjN2hFbXk1RlMvbWxlZCt2MFRFMlV5cGord1k0M0hvaEhpWjl3bVRECkpwTHZTKzgwTFpmMitrVkNBb05hTjdMdU1rdFJKaXlQUDc2TklSaUdPdzl6MmZpNHNIaUFnS0dvR1ZMb1c1YUMKa0RoK3dLKzh5NDVnVGZGR1VaWGpBa0pKSm1mWDd2TXllbkpyT2J5SUE0ajFuc294cDBNelFzNkYzREQ1TmdmZApQc1JtT3N6QlNLRTdNaFF4MEN5RlVQWjRTZ3U2N25MQytmRFBWVXBYY3pKQU1ZSTVrNWpyaElneDZnR2hKVFk0CmRLQ0VMWllwUmhCMWFFbVBIRjFlVUZ3MC9FcG5ldUdPd1ZqazZsSEp6QUxRUHBnR1dBZ0V1WFFVckxYb0dNclAKcWJrYU9WeitMelh0N1ZCaWJOZmdFUUlEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVTDlnL3FhZ2ptaGJ1K1pvQQpRVkFOdE1nd0cra3dId1lEVlIwakJCZ3dGb0FVTDlnL3FhZ2ptaGJ1K1pvQVFWQU50TWd3Rytrd0RBWURWUjBUCkJBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQW5Jd0Jyc3paY0pRRGFrZnNVVHp1eEtORmdUNUoKNVJOUWQ1S1NZM01HTWVkQjNIU2dMTEVWM3FTM1pLNHlrT3NFaXJ6c1lmdGV2Q1JGL2VsTkVIZDREQ2tiRzlSeQpwTzAwYTk1RkdFNktUWk1iSTRDaW1kMmF6eVhkazMyYUtzeFpjaDBRMzlneHgwSGFnVW5CcDk1VWxaQkcyOTh2CnhkQVRtSXZJNmVpd2FCeWc2NjBKRklRZ1VkVmhUM0VNTUI2dUlxaWdKZTJlMEcvV0t1My9BNGhvL3hDZUVNWmYKbFJlRXJIeFo0TzZsVTVEM0pnNURWcUM1MlBPK0V2aTJpdm5ZTmNvbTRibU9HbE41RmhzdG5La3M2ZlVsV012RQpXeXZCb01OU2VFNllueVFUUURBZ09BN0NlVzhKSUk4b3JRN00vM0JnWlNSOUZ4OGdhY01jeEUydTZRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURBRENDQWVnQ0NRRGljN284NjltaW16QU5CZ2txaGtpRzl3MEJBUXNGQURCQ01Rc3dDUVlEVlFRR0V3SlkKV0RFVk1CTUdBMVVFQnd3TVJHVm1ZWFZzZENCRGFYUjVNUnd3R2dZRFZRUUtEQk5FWldaaGRXeDBJRU52YlhCaApibmtnVEhSa01CNFhEVEl6TURZd09EQTVNak13TlZvWERUSTBNRFl3TnpBNU1qTXdOVm93UWpFTE1Ba0dBMVVFCkJoTUNXRmd4RlRBVEJnTlZCQWNNREVSbFptRjFiSFFnUTJsMGVURWNNQm9HQTFVRUNnd1RSR1ZtWVhWc2RDQkQKYjIxd1lXNTVJRXgwWkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTm5RUnVVaApnbWhtdFV4aitJQXpIayswdU5Oa2ZnUWFVMHpYdG1ZMGI3T0g3VkZXd2tVWDNJYVU2Q2JJRzd2MVRPbllISmZhCloyZmRvNURudlpaSk5OWnZOWUdEbkU4d3JuNmVHSlFpMW5hbDlEbGdyaWJPSWJtOUU4N055VUFTRXpkSEFhN1oKOUp1bEVlRlE5VXJHV25KbXhEZjBTSGM4ckJGRnBsMVpORXpUbDFpUFFhaDRDVGcya1lWMWJtS0h2cmhUcDV6UQp4VE4wT0ZUa1JKWmprN284YTRxUXhBVWYvU1ZkQ3BkeXBHYzhPM3JWL0dPMTdneFlVK2lmRmY0OW84M2l5ZGluCm0zQ3NmOFRFVWlNTGZqcDgwQk5ZQTVScVJPSG15NTVNSG9TM2VnWEJWOG5va3hvVHkzSy8rdnd0L21BMmVLQWEKVHRXeEo1NVM2T0M5R0xrQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBalYrWWpKSkU1c2o5ODZqQgpzenY3cXhkdXRGSHdIM1NpRXhzaEt0S3VpaE1BYjBJR1V4MEIxbmdTZ1gvNUVqUzEwYTZtRmhJZGxWS29PSDJ1CkQ3aVZtRkdweHBWaUtCTFMwVnhweGphVmZ0OExCd2k1cHN5eDZyWmEwaFMvek1NMEFlL1FuQXpoSzZDMDl5T08KN1g1R2orMjNQQjBVNkorZnRteThMYVpwK0ZBbWFobi9OYThJbmJNY1hEQjVEeEhNUWkzdjFrQUh6bnBGNU02KwpuSEkyR3B0RzR4UzlDTitFK2FBa3NBZGMzY2VjZ3JCL04vUFZNMWhFdkZtakw5SVpTdEJkYzBGQ1pGMHJPVy96CllhWVhLa3FRTm9Wa2FaMENsRVEybWdIMFh0ZktzQ0VZVGx0OGJncDgveDdKTmlqN2UvYkoyU0E0M015NTF1ZHYKZ0N5M1pRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBMmRCRzVTR0NhR2ExVEdQNGdETWVUN1M0MDJSK0JCcFRUTmUyWmpSdnM0ZnRVVmJDClJSZmNocFRvSnNnYnUvVk02ZGdjbDlwblo5MmprT2U5bGtrMDFtODFnWU9jVHpDdWZwNFlsQ0xXZHFYME9XQ3UKSnM0aHViMFR6czNKUUJJVE4wY0JydG4wbTZVUjRWRDFTc1phY21iRU4vUklkenlzRVVXbVhWazBUTk9YV0k5QgpxSGdKT0RhUmhYVnVZb2UrdUZPbm5OREZNM1E0Vk9SRWxtT1R1anhyaXBERUJSLzlKVjBLbDNLa1p6dzdldFg4Clk3WHVERmhUNko4Vi9qMmp6ZUxKMktlYmNLeC94TVJTSXd0K09uelFFMWdEbEdwRTRlYkxua3dlaExkNkJjRlgKeWVpVEdoUExjci82L0MzK1lEWjRvQnBPMWJFbm5sTG80TDBZdVFJREFRQUJBb0lCQUM4QVVLd1ZCUXorVE5VRgpKWlNVYzFBRDBYWmNVdzBUbVRJVndsaGZyRkx6Vy9TWFlpaUNzNldlOEZHZUVNNElhdVp6S2doaXFybXhEQ0N5CndTaHk5NkhtTVllWEhOM0J4WVd4RytDcmU5ZnlpN2J0OCthUHlKdEovOEk2aWRqM2pZbjZHcFRlbDNnV3NMc00KTzBJOWR6c0VqZ2I5QWI0cEs0QTJwV1d6WUNQTGh3cVVBTXBOS0tSc215NkQzWlozL2lHMmJ6TVRzdlpUUTQ1NQpZS1RCRkxzZGV2N1NLd3UxbWtUQzQrWkFPUjFqMFNSemVWdmFTSnczY2pWVm00SnRod3c1M1o4TVRzSFBIT3dLCmlGM1dlazdaK3BmZkpSTE91T2h3VnBzcTRlVnpCYXhXeW1QNkg0UmQxc1YwQWRGM3QwQlRmRnVudXBCa2RkM1kKUzY2UG5RRUNnWUVBNytCQXpjNDJxaDluQVgwekNhWGZlQU5GOG5OOFp4TTRUTXZEL0I3VWl2Ty9VNWR0Rm15Rgo4emhMbWpXZ3R4ZTdraFRma3R6czJ0TVc4UmhpRlZ0bGRtWForT0pqWFJtR1pwMkhLcmJ2R1pxTXdyLzFjdzJsClhkaTIwZHU0Slp1emxMM3ArSzhFMnRCdEFNK0tyZ09yUy9iTkVPRjg4NDFPUDhCRmIxNmtMakVDZ1lFQTZIUmcKbzNTaGpJcHU1NDNDOCtzbXNNRTFPZVVheEJ6YzdCNmg4a010SEhlRi8yOGVKQ3JJV0NnaDVtTWZCR3AxTmlmSgo2N0ZlT21NM0QyS0JzK3N5cVVQMVlKcVFCY0JKeUVuUzR5SnJpR2diQ1ZpUkpHdm95VGdEV21YbmpHKzI1N2ZHCnZaV0tSTGNKUHhWR0NrckdZc01BK3R1YzJJTE5LUkRPMlpRanlRa0NnWUE5YjVFSlpPUkpSQXVzclBVeVptSksKcVlQenFiSlY3KzArZGYyM0IrcGx3REhqWmVnUmt5L25jQ2FrMDFGYk0xL2Q5U3lodjZXR0VnUlJNVzZGaThmNwp2L0JJdHlxOXdIalV0VW5XSGM0MUg0a25vK1JvV0RsZlJNN21Cc0V1R0tldzA4Y2w0eVY2S1dHUmtKWXpKVXR0CkJFUFhLL2xGbzQ1RDg2bVU4WWRaTVFLQmdRRFN1UDBKOENhcWtxdXErUldyckpYc1VabUFuREhCYWpEVFU0bVgKWmxJMHBoMHd5M2hWYlBzay8yeUx2M3RVczNVQjNOdnM3Mkx1SnhhNHVhRytpZzNvNTVRL09KNHF1SCtxTTFJYgpXUTZHSDJteTlUak4vWXlQTEZuTnp1Y3lwZXIyNytBWDZNSHBQTXdEQmJQeWpJcCs2U3V3UFBsWVJHcmJPVU5xCmRpSmlrUUtCZ0Y0dWtWazFjRkFJazlOeThpNXlrNHR2QzY1SXk1dkQvYWFMeE0yWFo5dnN2TTc5TkNzbUp4VkwKYnlTeWxJbi9rWnFFT0tkRHkxZnRYWnY1aGsrUWhvUzNsT0xWTjlUZ2k0Unhqcnl6QmJDYXdQNjlBZmxxN3dsdwpJYzZFNlZncXJhOU52Q0Zxbm1PMzBaQ1NteENBUEJ3d2hqQmN1K1JEMFVxT0ZGMXZwckNlCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== -kind: Secret -metadata: - name: https-cert - namespace: kube-system -type: kubernetes.io/tls + host: http://${SERVER} + method: GET EOF -kubectl apply -f https-cert.yaml ``` -> notice: key body ca.crt tls.crt and tls.key are fixed field cannot be customized +### Check Task Status -## debug +After completing a round of tasks, you can use the kdoctor aggregate API to view the report for the current round. When the FINISH field is set to true, it indicates that all tasks have been completed, and you can access the overall report. -when something wrong happen, see the log for your task with following command ```shell -#get log -CRD_KIND="apphttphealthy" -CRD_NAME="httphealthy" -kubectl logs -n kube-system kdoctor-agent-v4vzx | grep -i "${CRD_KIND}.${CRD_NAME}" - +kubectl get apphttphealthy +NAME FINISH EXPECTEDROUND DONEROUND LASTROUNDSTATUS SCHEDULE +http true 1 1 succeed 0 1 ``` +* FINISH: indicate whether the task has been completed +* EXPECTEDROUND: number of expected task rounds +* DONEROUND: number of completed task rounds +* LASTROUNDSTATUS: execution status of the last round of tasks +* SCHEDULE: schedule rules for the task + + +### View Task Reports + +1. View existed reports + + ```shell + kubectl get kdoctorreport + NAME CREATED AT + http 0001-01-01T00:00:00Z + ``` + +2. View specific task reports + + The reports are aggregated from the agents running on both the kdoctor-control-plane node and the kdoctor-worker nodes after performing two rounds of stress testing respectively. + + ```shell + kubectl get kdoctorreport http -oyaml + apiVersion: system.kdoctor.io/v1beta1 + kind: KdoctorReport + metadata: + creationTimestamp: null + name: http + spec: + FailedRoundNumber: null + FinishedRoundNumber: 1 + Report: + - NodeName: kdoctor-control-plane + HttpAppHealthyTask: + Detail: + - MeanDelay: 10.317307 + Metrics: + Duration: 11.022081662s + EndTime: "2023-07-31T07:25:23Z" + Errors: {} + Latencies: + Max_inMx: 0 + Mean_inMs: 10.317307 + Min_inMs: 0 + P50_inMs: 0 + P90_inMs: 0 + P95_inMs: 0 + P99_inMs: 0 + RequestCounts: 104 + StartTime: "2023-07-31T07:25:12Z" + StatusCodes: + "200": 104 + SuccessCounts: 104 + TPS: 9.435604197939574 + TotalDataSize: 40040 byte + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetName: HttpAppHealthy target + TargetUrl: http://172.41.71.0 + Succeed: true + TargetNumber: 1 + TargetType: HttpAppHealthy + MaxCPU: 30.651% + MaxMemory: 97.00MB + HttpAppHealthyTaskSpec: + ... + PodName: kdoctor-agent-fmr9m + ReportType: agent test report + RoundDuration: 11.038965547s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-07-31T07:25:12Z" + EndTimeStamp: "2023-07-31T07:25:23Z" + TaskName: apphttphealthy.http + TaskType: AppHttpHealthy + - NodeName: kdoctor-worker + HttpAppHealthyTask: + Detail: + - MeanDelay: 10.548077 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetName: HttpAppHealthy target + TargetUrl: http://172.41.71.0 + Succeed: true + TargetNumber: 1 + TargetType: HttpAppHealthy + HttpAppHealthyTaskSpec: + ... + PodName: kdoctor-agent-s468h + ReportType: agent test report + RoundDuration: 11.034140236s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-07-31T07:25:12Z" + EndTimeStamp: "2023-07-31T07:25:23Z" + TaskName: apphttphealthy.http + TaskType: AppHttpHealthy + ReportRoundNumber: 1 + RoundNumber: 1 + Status: Finished + TaskName: http + TaskType: AppHttpHealthy + ``` + +> If the reports do not align with the expected results, check the MaxCPU and MaxMemory fields in the report to verify if there are available resources of the agents and adjust the resource limits for the agents accordingly. + +## Other Common Examples + +Below are examples of HTTP requests with bodies and HTTPS requests: + +1. Create an `AppHttpHealthy` task for HTTP with a body. This task will run continuously for 10 seconds. It will send POST requests with the provided body to the specified server at a rate of 10 QPS and be executed immediately. + + We are using the service address of the test server. If you have a different server address available, feel free to use it instead. + + Creating test body data + + ```shell + cat < 6m42s v1.27.1 - - ~# kubectll get po -n kdoctor - NAME READY STATUS RESTARTS AGE - kdoctor-controller-686b75d6d7-ktctx 1/1 Running 0 2m33s - ``` +```bash +~# kubectl get nodes +NAME STATUS ROLES AGE VERSION +kdoctor-control-plane Ready control-plane 7m3s v1.27.1 +kdoctor-worker Ready 6m42s v1.27.1 +~# kubectll get po -n kdoctor +NAME READY STATUS RESTARTS AGE +kdoctor-controller-686b75d6d7-ktctx 1/1 Running 0 2m33s +``` 接下来您可以根据您的需要进行任务的布置 [AppHttpHealthy](./apphttphealthy-zh_CN.md)、[NetReach](./netreach-zh_CN.md)、[NetDns](./netdns-zh_CN.md) diff --git a/docs/usage/install.md b/docs/usage/install.md index a0bd7071..329e94b3 100644 --- a/docs/usage/install.md +++ b/docs/usage/install.md @@ -49,15 +49,14 @@ helm install kdoctor kdoctor/kdoctor \ --set feature.aggregateReport.controller.pvc.storageClass=local-path \ --set feature.aggregateReport.controller.pvc.storageRequests="100Mi" \ --set feature.aggregateReport.controller.pvc.storageLimits="500Mi" -ðŸ "ðŸ "ðŸ "ðŸ "ðŸ "ðŸ "ñ ``` ### Verify that all components of kdoctor are running properly ```shell kubectl get pod -n kdoctor -NAME READY STATUS RESTARTS AGE -kdoctor-controller-686b75d6d7-k4dcq 1/1 Running 0 137m +NAME READY STATUS RESTARTS AGE +kdoctor-controller-686b75d6d7-k4dcq 1/1 Running 0 137m ``` ### Uninstall kdoctor diff --git a/docs/usage/netdns-zh_CN.md b/docs/usage/netdns-zh_CN.md index 28241c48..d7d4bd6a 100644 --- a/docs/usage/netdns-zh_CN.md +++ b/docs/usage/netdns-zh_CN.md @@ -4,25 +4,25 @@ ## 介绍 -对于这种任务, kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源 ,每一个 agent pod 都会向指定的 dns server 发送 dns 请求,默认并发量为 50 可覆盖多副本情况,并发量可在 kodcotr 的 configmap 中设置,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 +对于这种任务, kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源 ,每一个 agent Pod 都会向指定的 DNS server 发送 DNS 请求,默认并发量为 50 可覆盖多副本情况,并发量可在 kdoctor 的 configmap 中设置,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合 API 获取详细的报告。 -1.应用场景: -* 生产或 E2E 环境下,检测集群每个角落可访问 coreDNS 服务 -* 在应用部署阶段,用以配合调整 coredns 的资源和副本数量,以确认能够支撑期望的访问压力 -* 给 coredns 注入压力,配合 coredns 升级测试、混沌测试、bug 复现等目的 -* 测试集群外部的 dns 服务 +1. 应用场景: + * 生产或 E2E 环境下,检测集群每个角落可访问 CoreDNS 服务 + * 在应用部署阶段,用以配合调整 CoreDNS 的资源和副本数量,以确认能够支撑期望的访问压力 + * 给 CoreDNS 注入压力,配合 CoreDNS 升级测试、混沌测试、bug 复现等目的 + * 测试集群外部的 DNS 服务 -2.关于 NetDns CRD 的更多描述,可参考[NetDns](../reference/netdns-zh_CN.md) +2. 关于 NetDns CRD 的更多描述,可参考[NetDns](../reference/netdns-zh_CN.md) -3.功能列表: - -* 支持集群内外 dns server 测试 -* 支持 typeA 、typeAAAA 记录 -* 支持 udp、tcp、tcp-tls 协议 +3. 功能列表: -## 开始 + * 支持集群内外 DNS server 测试 + * 支持 typeA 、typeAAAA 记录 + * 支持 UDP、TCP、TCP-TLS 协议 -接下来将展示 `NetDns` 的使用示例 +## 使用步骤 + +接下来将展示 `NetDNS` 的使用示例 ### 安装 kdoctor @@ -30,7 +30,7 @@ ### 安装测试 server (选做) -kdoctor 官方仓库中包含了一个名为 server 的应用,内包含 http server,https server, dns server 可用来测试 kdoctor 功能,若存在其他测试的 server 可跳过安装。 +kdoctor 官方仓库中包含了一个名为 server 的应用,内包含 http server,https server, DNS server,可用来测试 kdoctor 功能,若存在其他测试的 server 可跳过安装。 ```shell helm repo add kdoctor https://kdoctor-io.github.io/kdoctor @@ -53,9 +53,9 @@ NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) server ClusterIP 172.41.71.0 80/TCP,443/TCP,53/UDP,53/TCP,853/TCP 2m31s ``` -### 创建 NetDns +### 创建 NetDns -创建 `NetDns` ,该任务将执行一轮持续 10s 的任务,任务会向集群内 dns server 以 qps 为 10 的速度使用 udp 协议,请求解析 `kubernetes.default.svc.cluster.local` 域名的 typeA 记录,并且立即执行。 +创建 `NetDns`,该任务将执行一轮持续 10s 的任务,任务会向集群内 DNS server 以 QPS 为 10 的速度使用 UDP 协议,请求解析 `kubernetes.default.svc.cluster.local` 域名的 typeA 记录,并且立即执行。 ```shell cat < 注:若报告与预期结果不符合,可关注报告中的 MaxCPU和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 - -## 集群外 dns server 测试 - -下面是携带 body 的 http 请求示例和 https 的请求示例 - -1.创建 `NetDns` 任务,该任务将执行一轮持续 10s 的任务,任务会向指定的 dns server 以 qps 为 10 的速度进行 udp 请求 `kubernetes.default.svc.cluster.local` 域名的 typeAAAA,并且立即执行。 - -这里使用 server 的 service 地址,若有其他 server 地址 可使用其他 server 地址。 - -创建 `NetDns` - -```shell -SERVER="172.41.71.0" -apiVersion: kdoctor.io/v1beta1 -kind: Netdns -metadata: - name: netdns- user -spec: - expect: - meanAccessDelayInMs: 1500 - successRate: 1 - request: - domain: kubernetes.default.svc.cluster.local - durationInSecond: 10 - perRequestTimeoutInMS: 1000 - protocol: udp - qps: 10 - schedule: - roundNumber: 1 - roundTimeoutMinute: 1 - schedule: 0 1 - target: - targetUser: - port: 53 - server: ${SERVER} -EOF -``` + - NodeName: kdoctor-worker + PodName: kdoctor-agent-krrnp + ReportType: agent test report + RoundDuration: 10.024533428s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-08-01T09:09:39Z" + EndTimeStamp: "2023-08-01T09:09:49Z" + TaskName: netdns.netdns-cluster + TaskType: Netdns + netDNSTask: + detail: + - FailureReason: null + MeanDelay: 0.58 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetName: typeA_172.41.0.10:53_kubernetes.default.svc.cluster.local + TargetProtocol: udp + TargetServer: 172.41.0.10:53 + succeed: true + targetNumber: 1 + targetType: kdoctor agent + MaxCPU: 30.651% + MaxMemory: 97.00MB + netDNSTaskSpec: + ... + ReportRoundNumber: 1 + RoundNumber: 1 + Status: Finished + TaskName: netdns-cluster + TaskType: Netdns + ``` + +> 若报告与预期结果不符合,可关注报告中的 MaxCPU和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 + +## 集群外 DNS server 测试 + +下面是携带 body 的 http 请求示例和 https 的请求示例: + +1. 创建 `NetDns` 任务,该任务将执行一轮持续 10s 的任务,任务会向指定的 DNS server 以 QPS 为 10 的速度进行 UDP 请求 `kubernetes.default.svc.cluster.local` 域名的 typeAAAA,并且立即执行。 + + 这里使用 server 的 service 地址,若有其他 server 地址 可使用其他 server 地址。 + + 创建 `NetDns` + + ```shell + SERVER="172.41.71.0" + apiVersion: kdoctor.io/v1beta1 + kind: Netdns + metadata: + name: netdns- user + spec: + expect: + meanAccessDelayInMs: 1500 + successRate: 1 + request: + domain: kubernetes.default.svc.cluster.local + durationInSecond: 10 + perRequestTimeoutInMS: 1000 + protocol: udp + qps: 10 + schedule: + roundNumber: 1 + roundTimeoutMinute: 1 + schedule: 0 1 + target: + targetUser: + port: 53 + server: ${SERVER} + EOF + ``` ## 环境清理 diff --git a/docs/usage/netdns.md b/docs/usage/netdns.md index 7353efa3..06285193 100644 --- a/docs/usage/netdns.md +++ b/docs/usage/netdns.md @@ -2,307 +2,256 @@ [**简体中文**](./netdns-zh_CN.md) | **English** -## concept +## Introduction -Fo this kind task, each kdoctor agent will send dns request to specified target, and get success rate and mean delay. -It could specify success condition to tell the result succeed or fail. -And, more detailed report will print to kdoctor agent stdout, or save to disc by kdoctor controller. +kdoctor-controller creates the necessary resources, including [agent](../concepts/runtime.md), based on the agentSpec. Each agent Pod sends DNS requests to a specified DNS server. By default, the concurrency level is set to 50, which can handle scenarios with multiple replicas. The concurrency level can be configured in the kdoctor configmap. The success rate and average latency are measured, and the results are evaluated based on predefined success criteria. Detailed reports can be obtained using the aggregate API. -the following is the spec of netdns +1. Use Cases: + * Test the accessibility of the CoreDNS service from all corners of the cluster in production or E2E environments. + * Adjust CoreDNS resources and replica count during application deployment to ensure it can handle the expected load. + * Apply stress to CoreDNS for purposes like testing upgrades, chaos tests, bug reproduction, etc + * Test external DNS services from within the cluster. -```shell - -cat < netdns.yaml -apiVersion: kdoctor.io/v1beta1 -kind: Netdns -metadata: - name: testdns -spec: - schedule: - schedule: "1 1" - roundNumber: 2 - roundTimeoutMinute: 1 - target: - targetDns: - testIPv4: true - testIPv6: false - serviceName: coredns - serviceNamespace: kube-system - targetUser: - server: 172.18.0.1 - port: 53 - request: - durationInSecond: 10 - qps: 20 - perRequestTimeoutInMS: 500 - domain: "kube-dns.kube-system.svc.cluster.local" - protocol: udp - expect: - successRate: 1 - meanAccessDelayInMs: 10000 -EOF - -kubectl apply -f netdns.yaml - -``` - -* spec.schedule: set how to schedule the task. - - roundNumber: how many rounds it should be to run this task - - schedule: Support Linux crontab syntax for scheduling tasks, while also supporting simple writing. - The first digit represents how long the task will start, and the second digit represents the interval time between each round of tasks, - separated by spaces. Example: "1 2" indicates that the task will start in 1 minute, and the interval time between each round of tasks. - - roundTimeoutMinute: the timeout in minute for each round, when the rask does not finish in time, it results to be failuire - - sourceAgentNodeSelector [optional]: set the node label selector, then, the kdoctor agent who locates on these nodes will implement the task. If not set this field, all kdoctor agent will execute the task - -* spec.request: how each kdoctor agent should send the dns request - - durationInSecond: for each round, the duration in second how long the dns request lasts - - perRequestTimeoutInMS: timeout in ms for each dns request +2. For more detailed information about the NetDns CRD, refer to [NetDns](../reference/netdns.md) - qps: qps - - domain: resolved domain +3. Features: -* spec.target: set the target of dns request. it could not set targetUser and targetDns at the same time + * Support testing DNS servers both inside and outside the cluster + * Supports typeA and typeAAAA records + * Supports UDP, TCP, and TCP-TLS protocols - targetUser [optional]: set an user-defined DNS server for the dns request +## Steps - server: the address for dns server +The following example demonstrates how to use `NetDNS`. - port: the port for dns server +### Install kdoctor - targetDns: [optional]: set cluster dns server for the dns request +Follow the [installation guide](./install.md) to install kdoctor. - testIPv4: test DNS server IPv4 address and request is type A. +### Install Test Server (Optional) - testIPv6: test DNS server IPv6 address and request is type AAAA. +The official kdoctor repository includes an application called "server" that contains an HTTP server, HTTPS server, and DNS server. This server can be employed to test the functionality of kdoctor. If you have other test servers available, you can skip this installation step. - serviceName: Specify the name of the DNS to be tested -* - serviceNamespace: Specify the namespace of the DNS to be tested - - protocol: Specify request protocol,Optional value udp,tcp,tcp-tls,default udp. - -* spec.expect: define the success condition of the task result - - meanAccessDelayInMs: mean access delay in MS, if the actual delay is bigger than this, it results to be failure - - successRate: the success rate of all dns requests. Notice, when a dns response code is >=200 and < 400, it's treated as success. if the actual whole success rate is smaller than successRate, the task results to be failure - -* status: the status of the task - doneRound: how many rounds have finished - - expectedRound: how many rounds the task expect - - finish: whether all rounds of this task have finished - - lastRoundStatus: the result of last round - - history: - roundNumber: the round number - - status: the status of this round - - startTimeStamp: when this round begins - - endTimeStamp: when this round finally finished - - duration: how long the round spent +```shell +helm repo add kdoctor https://kdoctor-io.github.io/kdoctor +helm repo update kdoctor +helm install server kdoctor/server -n kdoctor-test-server --wait --debug --create-namespace +``` - deadLineTimeStamp: the time deadline of a round +Check the status of test server - failedAgentNodeList: the node list where failed kdoctor agent locate +```shell +kubectl get pod -n kdoctor -owide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +server-7649566ff9-dv4jc 1/1 Running 0 76s 172.40.1.45 kdoctor-worker +server-7649566ff9-qc5dh 1/1 Running 0 76s 172.40.0.35 kdoctor-control-plane +``` - notReportAgentNodeList: the node list where uknown kdoctor agent locate. This means these agents have problems. +Obtain the service address of the test server - succeedAgentNodeList: the node list where successful kdoctor agent locate +```shell +kubectl get service -n kdoctor +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +server ClusterIP 172.41.71.0 80/TCP,443/TCP,53/UDP,53/TCP,853/TCP 2m31s +``` -## example +### Create NetDns -test custom dns server by crontab +Create `NetDns` object that will execute a 10-second continuous task. The task will send UDP requests to the cluster's internal DNS server at a rate of 10 QPS. It will request the typeA records whose domain name is `kubernetes.default.svc.cluster.local`. ```shell - -cat < netdns1.yaml +cat < netdns1.yaml -apiVersion: kdoctor.io/v1beta1 -kind: Netdns -metadata: - name: testdns -spec: - schedule: - schedule: "1 1" - roundNumber: 2 - roundTimeoutMinute: 1 - target: - protocol: udp - targetUser: - server: 172.18.0.1 - port: 53 request: + domain: kubernetes.default.svc.cluster.local durationInSecond: 10 + perRequestTimeoutInMS: 1000 + protocol: udp qps: 10 - perRequestTimeoutInMS: 500 - domain: "baidu.com" - expect: - successRate: 1 - meanAccessDelayInMs: 1000 -EOF - -kubectl apply -f netdns1.yaml - -``` - -test cluster dns server by crontab - -```shell - -cat < netdns.yaml -apiVersion: kdoctor.io/v1beta1 -kind: Netdns -metadata: - name: testdns -spec: schedule: - schedule: "*/1 * * * *" - roundNumber: 2 + roundNumber: 1 roundTimeoutMinute: 1 + schedule: 0 1 target: targetDns: + serviceName: kube-dns + serviceNamespace: kube-system testIPv4: true - testIPv6: false - serviceNamespaceName: kube-system/kube-dns - protocol: udp - request: - durationInSecond: 10 - qps: 20 - perRequestTimeoutInMS: 500 - domain: "kube-dns.kube-system.svc.cluster.local" - expect: - successRate: 1 - meanAccessDelayInMs: 10000 EOF - -kubectl apply -f netdns.yaml - ``` -test cluster dns server by simple - -```shell - -cat < netdns.yaml -apiVersion: kdoctor.io/v1beta1 -kind: Netdns -metadata: - name: testdns -spec: - schedule: - schedule: "1 1" - roundNumber: 2 - roundTimeoutMinute: 1 - target: - targetDns: - testIPv4: true - testIPv6: false - serviceNamespaceName: kube-system/test-app - protocol: udp - request: - durationInSecond: 10 - qps: 20 - perRequestTimeoutInMS: 500 - domain: "kube-dns.kube-system.svc.cluster.local" - expect: - successRate: 1 - meanAccessDelayInMs: 10000 -EOF +### Check Task Status -kubectl apply -f netdns.yaml +After completing a round of tasks, you can use the kdoctor aggregate API to view the report for the current round. When the FINISH field is set to true, it indicates that all tasks have been completed, and you can access the overall report. +```shell +kubectl get netdns +NAME FINISH EXPECTEDROUND DONEROUND LASTROUNDSTATUS SCHEDULE +netdns-cluster true 1 1 succeed 0 1 ``` +* FINISH: indicate whether the task has been completed +* EXPECTEDROUND: number of expected task rounds +* DONEROUND: number of completed task rounds +* LASTROUNDSTATUS: execution status of the last round of tasks +* SCHEDULE: schedule rules for the task + +### View Task Reports + +1. View existed reports + + ```shell + kubectl get kdoctorreport + NAME CREATED AT + netdns-cluster 0001-01-01T00:00:00Z + ``` + +2. View specific task reports + + The reports are aggregated from the agents running on both the kdoctor-control-plane node and the kdoctor-worker nodes after performing two rounds of stress testing respectively. + + ```shell + root@kdoctor-control-plane:/# kubectl get kdoctorreport netdns-cluster -oyaml + apiVersion: system.kdoctor.io/v1beta1 + kind: KdoctorReport + metadata: + creationTimestamp: null + name: netdns-cluster + spec: + FailedRoundNumber: null + FinishedRoundNumber: 1 + Report: + - NodeName: kdoctor-control-plane + PodName: kdoctor-agent-ntp9l + ReportType: agent test report + RoundDuration: 11.025723086s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-08-01T09:09:39Z" + EndTimeStamp: "2023-08-01T09:09:50Z" + TaskName: netdns.netdns-cluster + TaskType: Netdns + netDNSTask: + detail: + - FailureReason: null + MeanDelay: 0.2970297 + Metrics: + DNSMethod: udp + DNSServer: 172.41.0.10:53 + Duration: 11.002666395s + EndTime: "2023-08-01T09:09:50Z" + Errors: {} + FailedCounts: 0 + Latencies: + Max_inMx: 0 + Mean_inMs: 0.2970297 + Min_inMs: 0 + P50_inMs: 0 + P90_inMs: 0 + P95_inMs: 0 + P99_inMs: 0 + ReplyCode: + NOERROR: 101 + RequestCounts: 101 + StartTime: "2023-08-01T09:09:39Z" + SuccessCounts: 101 + TPS: 9.179593052634765 + TargetDomain: kubernetes.default.svc.cluster.local. + Succeed: true + SucceedRate: 1 + TargetName: typeA_172.41.0.10:53_kubernetes.default.svc.cluster.local + TargetProtocol: udp + TargetServer: 172.41.0.10:53 + succeed: true + targetNumber: 1 + targetType: kdoctor agent + MaxCPU: 30.651% + MaxMemory: 97.00MB + netDNSTaskSpec: + ... + - NodeName: kdoctor-worker + PodName: kdoctor-agent-krrnp + ReportType: agent test report + RoundDuration: 10.024533428s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-08-01T09:09:39Z" + EndTimeStamp: "2023-08-01T09:09:49Z" + TaskName: netdns.netdns-cluster + TaskType: Netdns + netDNSTask: + detail: + - FailureReason: null + MeanDelay: 0.58 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetName: typeA_172.41.0.10:53_kubernetes.default.svc.cluster.local + TargetProtocol: udp + TargetServer: 172.41.0.10:53 + succeed: true + targetNumber: 1 + targetType: kdoctor agent + MaxCPU: 30.651% + MaxMemory: 97.00MB + netDNSTaskSpec: + ... + ReportRoundNumber: 1 + RoundNumber: 1 + Status: Finished + TaskName: netdns-cluster + TaskType: Netdns + ``` + +> If the reports do not align with the expected results, check the MaxCPU and MaxMemory fields in the report to verify if there are available resources of the agents and adjust the resource limits for the agents accordingly. + +## Test an External DNS Server + +Below are examples of HTTP and HTTPS requests with body: + +1. Create `NetDns` object that will execute a 10-second continuous task. The task will send UDP requests to the cluster's internal DNS server at a rate of 10 QPS. It will request the typeA records whose domain name is `kubernetes.default.svc.cluster.local`. + + We are using the service address of the test server. If you have a different server address available, feel free to use it instead. + + Create `NetDns` + + ```shell + SERVER="172.41.71.0" + apiVersion: kdoctor.io/v1beta1 + kind: Netdns + metadata: + name: netdns- user + spec: + expect: + meanAccessDelayInMs: 1500 + successRate: 1 + request: + domain: kubernetes.default.svc.cluster.local + durationInSecond: 10 + perRequestTimeoutInMS: 1000 + protocol: udp + qps: 10 + schedule: + roundNumber: 1 + roundTimeoutMinute: 1 + schedule: 0 1 + target: + targetUser: + port: 53 + server: ${SERVER} + EOF + ``` + +## Environment Cleanup -## report - -when the kdoctor is not enabled to aggerate reports, all reports will be printed in the stdout of kdoctor agent. -Use the following command to get its report ```shell -kubectl logs -n kube-system kdoctor-agent-lwhtm | jq 'select( .TaskName=="netdns.testdns" )' +kubectl delete netdns netdns-cluster netdns- user ``` - -when the kdoctor is enabled to aggregate reports, all reports will be collected in the PVC or hostPath of kdoctor controller. - - -metric introduction -```json -{ - "TaskName": "netdns.testdns", - "TaskSpec": { - "schedule": { - "schedule": "1 1", - "roundTimeoutMinute": 1, - "roundNumber": 2 - }, - "target": { - "protocol": "tcp" - }, - "request": { - "durationInSecond": 10, - "qps": 20, - "perRequestTimeoutInMS": 500, - "domain": "kube-dns.kube-system.svc.cluster.local" - }, - "success": { - "successRate": 1, - "meanAccessDelayInMs": 10000 - } - }, - "RoundNumber": 1, - "RoundResult": "succeed", - "NodeName": "kdoctor-control-plane", - "PodName": "kdoctor-agent-lwhtm", - "FailedReason": "", - "StartTimeStamp": "2023-04-27T07:07:32.032814878Z", - "EndTimeStamp": "2023-04-27T07:07:32.070513569Z", - "RoundDuraiton": "37.69869ms", - "ReportType": "agent test report", - "Detail": {} -} -``` \ No newline at end of file diff --git a/docs/usage/netreach-zh_CN.md b/docs/usage/netreach-zh_CN.md index 5a234df9..916519a9 100644 --- a/docs/usage/netreach-zh_CN.md +++ b/docs/usage/netreach-zh_CN.md @@ -4,21 +4,22 @@ ## 介绍 -对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent pod 都会以一定的压力相互发送http请求,请求地址为每一个 agent 的 pod ip 、service ip、ingress ip 等等,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 +对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent Pod 都会以一定的压力相互发送http 请求,请求地址为每一个 agent 的 Pod ip 、service ip、ingress ip 等等,并获得成功率和平均延迟。根据成功条件来判断结果是否成功。并且,可以通过聚合 API 获取详细的报告。 -1.应用场景: -* 每1min心跳,监控集群内每个角落的连通性 -* 大规模集群部署后,巡检集群每个角落的连通性 -* 给集群所有角落、每种通信方式注入流量,配合 bug 复现等场景 -* 生产或 E2E 环境下,巡检 CNI、Loadbalancer、kube-proxy、Ingress、Multus 等组件的异常。 +1. 应用场景: -2.关于 NetReach CRD 的更多描述,可参考[NetReach](../reference/netreach-zh_CN.md) + * 每 1min 心跳,监控集群内每个角落的连通性 + * 大规模集群部署后,巡检集群每个角落的连通性 + * 给集群所有角落、每种通信方式注入流量,配合 bug 复现等场景 + * 生产或 E2E 环境下,巡检 CNI、Loadbalancer、kube-proxy、Ingress、Multus 等组件的异常。 -3.功能列表: - -* 支持 ClusterIP、Endpoint、Ingress、NodePort、LoadBalancer、Multus 多网卡、IPv4 IPv6 +2. 关于 NetReach CRD 的更多描述,可参考[NetReach](../reference/netreach-zh_CN.md) -## 开始 +3. 功能列表: + + 支持 ClusterIP、Endpoint、Ingress、NodePort、LoadBalancer、Multus 多网卡、IPv4 IPv6 + +## 使用步骤 接下来将展示 `NetReach` 的使用示例 @@ -75,191 +76,190 @@ task true 1 1 succeed 0 1 * LASTROUNDSTATUS:最后一轮任务执行情况 * SCHEDULE:任务的调度规则 - ### 查看任务报告 -1.查看已有报告 +1. 查看已有报告 -```shell -kubectl get kdoctorreport -NAME CREATED AT -task 0001-01-01T00:00:00Z -``` + ```shell + kubectl get kdoctorreport + NAME CREATED AT + task 0001-01-01T00:00:00Z + ``` -2.查看具体任务报告 +2. 查看具体任务报告 -节点 kdoctor-control-plane 和节点 kdoctor-worker 上 agent 分别都执行一轮互相发压后,将 agent 报告聚合而成。 + 节点 kdoctor-control-plane 和节点 kdoctor-worker 上 agent 分别都执行一轮互相发压后,将 agent 报告聚合而成。 -```shell -root@kdoctor-control-plane:/# kubectl get kdoctorreport task -oyaml -apiVersion: system.kdoctor.io/v1beta1 -kind: KdoctorReport -metadata: - creationTimestamp: null - name: task -spec: - FailedRoundNumber: null - FinishedRoundNumber: 1 - Report: - - NodeName: kdoctor-control-plane - NetReachTask: - Detail: - - TargetName: AgentLoadbalancerV4IP_172.18.0.51:80 - Metrics: - Duration: 10.032286878s - EndTime: "2023-08-01T08:37:06Z" - Errors: {} - Latencies: - Max_inMx: 0 - Mean_inMs: 23.08 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 - RequestCounts: 100 - StartTime: "2023-08-01T08:36:56Z" - StatusCodes: - "200": 100 - SuccessCounts: 100 - TPS: 9.967817030760152 - TotalDataSize: 36968 byte - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.51:80 - MeanDelay: 23.08 - - TargetName: AgentNodePortV4IP_172.18.0.3_32713 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.3:32713 - MeanDelay: 68.42 - - TargetName: AgentPodV4IP_kdoctor-agent-ntp9l_172.40.0.6 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.40.0.6:80 - MeanDelay: 44.049503 - - TargetName: AgentClusterV4IP_172.41.249.6:80 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.41.249.6:80 - MeanDelay: 26.307692 - - TargetName: AgentPodV4IP_kdoctor-agent-krrnp_172.40.1.5 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.40.1.5:80 - MeanDelay: 61.564358 - - TargetName: AgentIngress_http://172.18.0.50/kdoctoragent - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.50/kdoctoragent - MeanDelay: 65.47059 - Succeed: true - TargetNumber: 6 - TargetType: NetReach - MaxCPU: 26.203% - MaxMemory: 101.00MB - NetReachTaskSpec: - ... - PodName: kdoctor-agent-ntp9l - ReportType: agent test report - RoundDuration: 11.178657432s - RoundNumber: 1 - RoundResult: succeed - StartTimeStamp: "2023-08-01T08:36:56Z" - EndTimeStamp: "2023-08-01T08:37:07Z" - TaskName: netreach.task - TaskType: NetReach - - NodeName: kdoctor-worker - NetReachTask: - Detail: - - TargetName: AgentClusterV4IP_172.41.249.6:80 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.41.249.6:80 - MeanDelay: 47.25 - - TargetName: AgentNodePortV4IP_172.18.0.2_32713 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.2:32713 - MeanDelay: 13.480392 - - TargetName: AgentPodV4IP_kdoctor-agent-krrnp_172.40.1.5 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.40.1.5:80 - MeanDelay: 39.637257 - - TargetName: AgentPodV4IP_kdoctor-agent-ntp9l_172.40.0.6 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.40.0.6:80 - MeanDelay: 51.38614 - - TargetName: AgentLoadbalancerV4IP_172.18.0.51:80 - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.51:80 - MeanDelay: 41.735847 - - TargetName: AgentIngress_http://172.18.0.50/kdoctoragent - Metrics: - ... - Succeed: true - SucceedRate: 1 - TargetMethod: GET - TargetUrl: http://172.18.0.50/kdoctoragent - MeanDelay: 60.463634 - Succeed: true - TargetNumber: 6 - TargetType: NetReach - MaxCPU: 30.651% - MaxMemory: 97.00MB - NetReachTaskSpec: - ... - PodName: kdoctor-agent-krrnp - ReportType: agent test report - RoundDuration: 11.180813761s - RoundNumber: 1 - RoundResult: succeed - StartTimeStamp: "2023-08-01T08:36:56Z" - EndTimeStamp: "2023-08-01T08:37:07Z" - TaskName: netreach.task - TaskType: NetReach - ReportRoundNumber: 1 - RoundNumber: 1 - Status: Finished - TaskName: task - TaskType: NetReach -``` + ```shell + root@kdoctor-control-plane:/# kubectl get kdoctorreport task -oyaml + apiVersion: system.kdoctor.io/v1beta1 + kind: KdoctorReport + metadata: + creationTimestamp: null + name: task + spec: + FailedRoundNumber: null + FinishedRoundNumber: 1 + Report: + - NodeName: kdoctor-control-plane + NetReachTask: + Detail: + - TargetName: AgentLoadbalancerV4IP_172.18.0.51:80 + Metrics: + Duration: 10.032286878s + EndTime: "2023-08-01T08:37:06Z" + Errors: {} + Latencies: + Max_inMx: 0 + Mean_inMs: 23.08 + Min_inMs: 0 + P50_inMs: 0 + P90_inMs: 0 + P95_inMs: 0 + P99_inMs: 0 + RequestCounts: 100 + StartTime: "2023-08-01T08:36:56Z" + StatusCodes: + "200": 100 + SuccessCounts: 100 + TPS: 9.967817030760152 + TotalDataSize: 36968 byte + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.51:80 + MeanDelay: 23.08 + - TargetName: AgentNodePortV4IP_172.18.0.3_32713 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.3:32713 + MeanDelay: 68.42 + - TargetName: AgentPodV4IP_kdoctor-agent-ntp9l_172.40.0.6 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.40.0.6:80 + MeanDelay: 44.049503 + - TargetName: AgentClusterV4IP_172.41.249.6:80 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.41.249.6:80 + MeanDelay: 26.307692 + - TargetName: AgentPodV4IP_kdoctor-agent-krrnp_172.40.1.5 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.40.1.5:80 + MeanDelay: 61.564358 + - TargetName: AgentIngress_http://172.18.0.50/kdoctoragent + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.50/kdoctoragent + MeanDelay: 65.47059 + Succeed: true + TargetNumber: 6 + TargetType: NetReach + MaxCPU: 26.203% + MaxMemory: 101.00MB + NetReachTaskSpec: + ... + PodName: kdoctor-agent-ntp9l + ReportType: agent test report + RoundDuration: 11.178657432s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-08-01T08:36:56Z" + EndTimeStamp: "2023-08-01T08:37:07Z" + TaskName: netreach.task + TaskType: NetReach + - NodeName: kdoctor-worker + NetReachTask: + Detail: + - TargetName: AgentClusterV4IP_172.41.249.6:80 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.41.249.6:80 + MeanDelay: 47.25 + - TargetName: AgentNodePortV4IP_172.18.0.2_32713 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.2:32713 + MeanDelay: 13.480392 + - TargetName: AgentPodV4IP_kdoctor-agent-krrnp_172.40.1.5 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.40.1.5:80 + MeanDelay: 39.637257 + - TargetName: AgentPodV4IP_kdoctor-agent-ntp9l_172.40.0.6 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.40.0.6:80 + MeanDelay: 51.38614 + - TargetName: AgentLoadbalancerV4IP_172.18.0.51:80 + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.51:80 + MeanDelay: 41.735847 + - TargetName: AgentIngress_http://172.18.0.50/kdoctoragent + Metrics: + ... + Succeed: true + SucceedRate: 1 + TargetMethod: GET + TargetUrl: http://172.18.0.50/kdoctoragent + MeanDelay: 60.463634 + Succeed: true + TargetNumber: 6 + TargetType: NetReach + MaxCPU: 30.651% + MaxMemory: 97.00MB + NetReachTaskSpec: + ... + PodName: kdoctor-agent-krrnp + ReportType: agent test report + RoundDuration: 11.180813761s + RoundNumber: 1 + RoundResult: succeed + StartTimeStamp: "2023-08-01T08:36:56Z" + EndTimeStamp: "2023-08-01T08:37:07Z" + TaskName: netreach.task + TaskType: NetReach + ReportRoundNumber: 1 + RoundNumber: 1 + Status: Finished + TaskName: task + TaskType: NetReach + ``` -> 注:若报告与预期结果不符合,可关注报告中的 MaxCPU和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 +> 若报告与预期结果不符合,可关注报告中的 MaxCPU 和 MaxMemory 字段,对比 agent 资源是否充足,调整 agent 的资源限制。 ## 环境清理 diff --git a/docs/usage/netreach.md b/docs/usage/netreach.md index c02090a3..8b22de5b 100644 --- a/docs/usage/netreach.md +++ b/docs/usage/netreach.md @@ -2,229 +2,267 @@ [**简体中文**](./netreach-zh_CN.md) | **English** -## concept +## Introduction -Fo this kind task, each kdoctor agent will send http request to specified target, and get success rate and mean delay. -It could specify success condition to tell the result succeed or fail. -And, more detailed report will print to kdoctor agent stdout, or save to disc by kdoctor controller. +kdoctor-controller creates the necessary resources, including [agent](../concepts/runtime.md), based on the agentSpec. Each agent Pod sends HTTP requests to each other with a certain level of stress. These requests target various addresses such as Pod IP, service IP, ingress IP, and more. The success rate and average latency are measured, and the results are evaluated based on predefined success criteria. Detailed reports can be obtained using the aggregate API. -the following is the spec of nethttp -```shell -apiVersion: kdoctor.io/v1beta1 -kind: NetReach -metadata: - creationTimestamp: "2023-05-24T08:11:13Z" - generation: 1 - name: netreach - resourceVersion: "1427617" - uid: 2f5da0d6-0252-4229-adca-a43a5d2ac4ff -spec: - request: - durationInSecond: 10 - perRequestTimeoutInMS: 1000 - qps: 10 - schedule: - roundNumber: 2 - roundTimeoutMinute: 1 - schedule: 1 1 - expect: - meanAccessDelayInMs: 10000 - successRate: 1 - target: - clusterIP: true - endpoint: true - ingress: false - ipv4: true - ipv6: true - loadBalancer: false - multusInterface: false - nodePort: true -status: - doneRound: 2 - expectedRound: 2 - finish: true - history: - - deadLineTimeStamp: "2023-05-24T08:14:13Z" - duration: 20.089468009s - endTimeStamp: "2023-05-24T08:13:33Z" - expectedActorNumber: 2 - failedAgentNodeList: [] - notReportAgentNodeList: [] - roundNumber: 2 - startTimeStamp: "2023-05-24T08:13:13Z" - status: succeed - succeedAgentNodeList: - - kdoctor-worker - - kdoctor-control-plane -``` - -* spec.schedule: set how to schedule the task. - - roundNumber: how many rounds it should be to run this task - - schedule: Support Linux crontab syntax for scheduling tasks, while also supporting simple writing. - The first digit represents how long the task will start, and the second digit represents the interval time between each round of tasks, - separated by spaces. Example: "1 2" indicates that the task will start in 1 minute, and the interval time between each round of tasks. - - roundTimeoutMinute: the timeout in minute for each round, when the rask does not finish in time, it results to be failuire - - sourceAgentNodeSelector [optional]: set the node label selector, then, the kdoctor agent who locates on these nodes will implement the task. If not set this field, all kdoctor agent will execute the task - -* spec.request: how each kdoctor agent should send the http request - - durationInSecond: for each round, the duration in second how long the http request lasts - - perRequestTimeoutInMS: timeout in ms for each http request - - qps: qps - -* spec.target: set the target of http request. it could not set targetUser and targetAgent at the same time +1. Use cases: - target: [optional]: set the http tareget to kdoctor agents + * Monitor connectivity in every corner of the cluster with a 1-minute heartbeat interval. + * Perform connectivity inspections across all corners of the cluster after deploying at scale + * Inject traffic to all corners of the cluster using different communication methods for scenarios like bug reproduction + * Inspect anomalies in components such as CNI, LoadBalancer, kube-proxy, Ingress, Multus, etc., in production or E2E environments. - clusterIP: send http request to the cluster ipv4 or ipv6 address of kdoctor agnent, according to ipv4 and ipv6. +2. For more information on NetReach CRD, refer to[NetReach](../reference/netreach.md) - endpoint: send http request to other kdoctor agnent ipv4 or ipv6 address according to testIPv4 and testIPv6. +3. Features: - multusInterface: whether send http request to all interfaces ip in testEndpoint case. + Support ClusterIP, Endpoint, Ingress, NodePort, LoadBalancer, Multus with multiple network interfaces, IPv4, and IPv6. - ipv4: test any IPv4 address. Notice, the 'enableIPv4' in configmap spiderdocter must be enabled +## Steps - ipv6: test any IPv6 address. Notice, the 'enableIPv6' in configmap spiderdocter must be enabled +The following example demonstrates how to use `NetReach`. - ingress: send http request to the ingress ipv4 or ipv6 address of kdoctor agnent +### Install kdoctor - nodePort: send http request to the nodePort ipv4 or ipv6 address with each local node of kdoctor agnent , according to testIPv4 and testIPv6. +Follow the [installation guide](./install.md) to install kdoctor. - >notice: when test targetAgent case, it will send http request to all targets at the same time with spec.request.qps for each one. That meaning, the actually QPS may be bigger than spec.request.qps +### Create NetReach -* spec.expect: define the success condition of the task result - - meanAccessDelayInMs: mean access delay in MS, if the actual delay is bigger than this, it results to be failure - - successRate: the success rate of all http requests. Notice, when a http response code is >=200 and < 400, it's treated as success. if the actual whole success rate is smaller than successRate, the task results to be failure - -* status: the status of the task - doneRound: how many rounds have finished - - expectedRound: how many rounds the task expect - - finish: whether all rounds of this task have finished - - lastRoundStatus: the result of last round - - history: - roundNumber: the round number - - status: the status of this round - - startTimeStamp: when this round begins - - endTimeStamp: when this round finally finished - - duration: how long the round spent - - deadLineTimeStamp: the time deadline of a round - - failedAgentNodeList: the node list where failed kdoctor agent locate - - notReportAgentNodeList: the node list where uknown kdoctor agent locate. This means these agents have problems. - - succeedAgentNodeList: the node list where successful kdoctor agent locate - - -## example - -a quick task to test kdoctor agent, to verify the whole network is ok, each agent could reach each other +Create `NetReach` object that will execute a 10-second continuous task. Each agent on the nodes will use the HTTP protocol to access the IPv4 addresses of ClusterIP, Endpoint, Ingress, NodePort, and LoadBalancer immediately. ```shell - -cat < netreachhealthy-test-agent.yaml +cat < If the reports do not align with the expected results, check the MaxCPU and MaxMemory fields in the report to verify if there are available resources of the agents and adjust the resource limits for the agents accordingly. + +## Environment Cleanup -metric introduction ```shell - { - "FailureReason": "", - "MeanDelay": 106.84, - "Metrics": { - "start": "2023-05-24T08:13:13.530015031Z", - "end": "2023-05-24T08:13:28.560982373Z", - "duration": "15.030967342s", - "requestCount": 150, - "successCount": 150, - "tps": 9.979397638691244, - "total_request_data": "34866 byte", - "latencies": { - "P50_inMs": 103, - "P90_inMs": 199, - "P95_inMs": 202, - "P99_inMs": 204, - "Max_inMx": 205, - "Min_inMs": 3, - "Mean_inMs": 106.84 - }, - "status_codes": { - "200": 150 - }, - "errors": {} - }, - "Succeed": "true", - "SucceedRate": "1", - "TargetMethod": "GET", - "TargetName": "AgentClusterV4IP_172.41.156.187:80", - "TargetUrl": "http://172.41.156.187:80" - } +kubectl delete netreach task ``` From df7eb6cc17a4a3b3a8d3f28d7d3877f906146903 Mon Sep 17 00:00:00 2001 From: Jeanine-tw <76861242+Jeanine-tw@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:08:16 +0800 Subject: [PATCH 10/41] doc: update en usage and concept --- docs/concepts/runtime-zh_CN.md | 51 ++++-------------- docs/concepts/runtime.md | 75 +++++++++------------------ docs/images/lifecycle-zh.png | Bin 0 -> 221886 bytes docs/images/lifecycle.png | Bin 0 -> 191570 bytes docs/usage/get-started-kind-zh_CN.md | 6 ++- docs/usage/get-started-kind.md | 20 +++---- docs/usage/install-zh_CN.md | 8 +-- docs/usage/install.md | 22 ++++---- docs/usage/performance.md | 40 +++++++------- 9 files changed, 85 insertions(+), 137 deletions(-) create mode 100644 docs/images/lifecycle-zh.png create mode 100644 docs/images/lifecycle.png diff --git a/docs/concepts/runtime-zh_CN.md b/docs/concepts/runtime-zh_CN.md index 8067ed3f..56ba5104 100644 --- a/docs/concepts/runtime-zh_CN.md +++ b/docs/concepts/runtime-zh_CN.md @@ -6,28 +6,27 @@ ### 载体资源 -当任务 CR 下发后,kdocotr-controller 会创建或复用如下资源进行任务。 +当任务 CR 下发后,kdocotr-controller 会创建或复用如下资源执行任务。 ### 工作负载 1. 默认工作负载 - >默认工作负载(DaemonSet)在部署 kdoctor 后生成,在未定义 AgentSpec 时,使用此载体进行任务,此载体不会因为任务删除或结束而被删除。 + > 默认工作负载(DaemonSet)在部署 kdoctor 后生成,在未定义 AgentSpec 时,使用此载体执行任务,此载体不会因为任务删除或结束而被删除。 - >因所有使用默认工作负载的任务都会在此负载中执行,因此适合请求量较少,资源使用较少的任务。 + > 因所有使用默认工作负载的任务都会在此负载中执行,因此适合请求量较少,资源使用较少的任务。 2. 新建工作负载 - >工作负载为 DaemonSet 或 Deployment,默认为 DaemonSet,负载中的每一个 Pod 根据任务配置进行的请求,并将执行结果落盘到 Pod 中,可通过 AgentSpec 中设置 - > 工作负载的销毁时间,默认任务执行完 60 分钟后,销毁工作负载,当删除 CR 任务时,工作负载会一并被删除。 + > 工作负载为 DaemonSet 或 Deployment,默认为 DaemonSet,负载中的每一个 Pod 根据任务配置执行请求,并将执行结果落盘到 Pod 中,可通过 AgentSpec 中设置 + 工作负载的销毁时间,默认任务执行完 60 分钟后,销毁工作负载,当删除 CR 任务时,工作负载会一并被删除。 - >此工作负载单独执行一个任务,因此与其他任务的资源使用是隔离的,适合请求量较大,资源消耗较大的任务。 + > 此工作负载单独执行一个任务,因此与其他任务的资源使用是隔离的,适合请求量较大,资源消耗较大的任务。 ### Service 1. 默认工作载体 Service >与默认工作负载一样,在部署 kdoctor 后生成,与默认负载关联且不会因为任务删除或结束而被删除。 2. 新建工作载体 Service - >在创建工作负载时,kdoctor-controller 同时会根据 IP Family 的配置,创建对应的 service 并于工作负载的 pod 绑定。用于测试 service 网络连通性。与工作负载 - >的销毁逻辑相同。 + >在创建工作负载时,kdoctor-controller 同时会根据 IP Family 的配置,创建对应的 service 与工作负载的 Pod 绑定。用于测试 service 网络连通性。与工作负载的销毁逻辑相同。 ### Ingress @@ -45,39 +44,9 @@ ### 生命周期 -```mermaid -sequenceDiagram - participant cr 任务 - participant kdoctor_controller - participant workload - participant pod - participant service - participant ingress - - cr 任务 ->>kdoctor_controller: cr 任务下发 - kdoctor_controller ->>workload: 创建 ownerReferences 为任务 cr 工作负载(daemonSet 或 Deployment) - workload ->>pod: 创建任务执行 pod - kdoctor_controller ->>service: 创建 ownerReferences 为工作负载的 service - kdoctor_controller ->>ingress: 创建 ownerReferences 为工作负载的 ingress - workload ->>kdoctor_controller: workload 就绪 - service ->>kdoctor_controller: service 就绪 - ingress ->>kdoctor_controller: ingress 就绪 - kdoctor_controller ->>pod: 任务执行 - kdoctor_controller ->>pod: 定时收取报告 - pod ->>kdoctor_controller: 任务执行完成 - kdoctor_controller ->>pod: 报告收取完成 - pod ->>pod: 报告收取完成默认 10 分钟后,自动清理报告 - kdoctor_controller ->>workload: 到达 runtime 销毁时间,销毁 workload - workload ->>service: 到达 runtime 销毁时间,销毁 service - workload ->>ingress: 到达 runtime 销毁时间,销毁 ingress - cr 任务 ->>kdoctor_controller: cr 任务删除 - kdoctor_controller ->> workload: cr 任务删除,workload 删除 - workload ->> pod: workload 删除,pod 删除 - workload ->>service: workload 删除,service 删除 - workload ->>ingress: workload 删除,ingress 删除 -``` - -* 任务开始后,kdoctor-controller 会定时向任务中的 pod 收取报告,任务完成后,报告收集完成,不会再进行报告收集。 +![生命周期](../images/lifecycle-zh.png) + +* 任务开始后,kdoctor-controller 会定时向任务中的 Pod 收取报告,任务完成后,报告收集完成,不会再进行报告收集。 * 任务负载执行完任务后,报告被 kdoctor-controller 收取报告默认 10 分钟后,会自动清理掉负载中的报告。 * 当删除掉已经完成的任务 CR 后,报告依然存在 kdoctor-controller 报告目录下,但无法通过 k8s 聚合 api 查看,需要手动才能进行查看。 * 当删除执行中的任务 CR 时,任务会终止,创建 CR 时生成的资源会一并删除,已经收集好的报告依然存放在 kdoctor-controller 报告目录下。 \ No newline at end of file diff --git a/docs/concepts/runtime.md b/docs/concepts/runtime.md index 62dd6ed9..beb3920f 100644 --- a/docs/concepts/runtime.md +++ b/docs/concepts/runtime.md @@ -2,74 +2,47 @@ [**简体中文**](./runtime-zh_CN.md) | **English** -After deploying kdoctor, a default task carrier (Daemonset) will be created. When a task CR is issued, kdoctor-controller will choose to generate the corresponding task carrier (DaemonSet or Deployment) or use the default carrier resources based on whether or not the AgentSpec field is defined in the CR. When all Pods are ready, they start executing tasks according to the task definitions in the Spec. +After deploying kdoctor, a default task carrier (Daemonset) will be created. When a task CR is delivered, kdoctor-controller will choose to generate the corresponding task carrier (DaemonSet or Deployment) or use the default carrier resources based on whether or not the AgentSpec field is defined in the CR. When all Pods are ready, they start executing tasks according to the task definitions in the Spec. -### Carrier resources +### Carrier Resources -When a task CR is issued, kdoctor-controller creates or reuses the following resources for the task. +When a task CR is delivered, kdoctor-controller creates or reuses the following resources to execute the task. ### Workloads 1. Default workload - > The default workload (DaemonSet) is generated after kdoctor is deployed and is used for tasks when no AgentSpec is defined; this carrier is not deleted for task deletion or termination. - > Since all tasks that use the default workload will be executed in this load, it is suitable for tasks with fewer requests and less resource usage. + > The default workload (DaemonSet) is generated after kdoctor is deployed and is used for executing tasks when no AgentSpec is defined; this carrier is not deleted for task deletion or termination. + > Since all tasks that use the default workload will be executed in this workload, it is suitable for tasks with fewer requests and less resource usage. 2. New workload - > The workload is DaemonSet or Deployment, the default is DaemonSet, each Pod in the load makes the request according to the task configuration and drops the execution result to the Pod, which can be set in AgentSpec. - > Destruction time of the workload, by default, the workload will be destroyed after 60 minutes of task execution, when the CR task is deleted, the workload will be deleted together. - When the CR task is deleted, the workload will be deleted as well. > This workload executes a task alone, so it is isolated from the resource usage of other tasks, which is suitable for tasks with a large number of requests and resource consumption. + > The workload is DaemonSet or Deployment, and the former is the default. Each Pod in the workload executes the request according to the task configuration and drops the execution result to the Pod. Destruction time of the workload can be set in AgentSpec. + By default, the workload will be destroyed after 60 minutes of task execution. When the CR task is deleted, the workload will be deleted as well. + + > This workload executes a task alone, so it is isolated from the resource usage of other tasks, which is suitable for tasks with a large number of requests and resource consumption. ### Service 1. Default workload Service > Same as the default workload, it is generated after deploying kdoctor, associated with the default workload, and will not be deleted when a task is deleted or terminated. -2. new workload Service - >When creating a workload, kdoctor-controller will create a corresponding service and bind it to the pod of the workload according to the IP Family configuration. This is used to test the service network connectivity. Same logic as workload - > destruction logic. +2. New workload Service + >When creating a workload, kdoctor-controller will create a corresponding service and bind it to the Pod of the workload according to the IP Family configuration. This is used to test the service network connectivity, which is the same logic as workload + destruction logic. ### Ingress -1. default workload Ingress - >is generated after deployment of the kdoctor and is associated with the default workload service and will not be deleted when the task is deleted or terminated, as is the case with the default workload. -2. new workload Ingress - >When the task is NetReach, if the test target contains an Ingress, an Ingress is created to test the network connectivity of the Ingress, with the same destruction logic as the workload. +1. Default workload Ingress + > The default workload Ingress is generated after deployment of the kdoctor and is associated with the default workload service and will not be deleted when the task is deleted or terminated, as is the case with the default workload. +2. New workload Ingress + >When the task is NetReach and the test target contains an Ingress, an Ingress is created to test the network connectivity of the Ingress, with the same destruction logic as the workload. + ### Report Collection -When the task CR is issued, kdoctor-controller will register the task into ReportManager, which will periodically go to each task load to get the report via GRPC interface and aggregate the reports into the kdoctor-controller. -In kdoctor-controller, after aggregation, you can get the report result by command `kubectl get kdoctorreport`, therefore, if you delete the workload before the report is collected, it will affect the report aggregation result. +When the task CR is delivered, kdoctor-controller will register the task into ReportManager. ReportManager will periodically go to each task workload to get the report via GRPC interface and aggregate the reports into the kdoctor-controller. +In kdoctor-controller, after aggregation, you can get the report result by command `kubectl get kdoctorreport`. Therefore, if you delete the workload before the report is collected, it will affect the report aggregation result. ### Lifecycle -```mermaid -sequenceDiagram - participant cr Task - participant kdoctor_controller - participant workload - participant pod - participant service - participant ingress - cr task ->>kdoctor_controller: cr task to distribute - kdoctor_controller ->>workload: create ownerReferences for task cr workload (daemonSet or Deployment) - workload ->>pod: create a task execution pod - kdoctor_controller ->>service: creates a service with ownerReferences for the workload - kdoctor_controller ->>ingress: Create an ingress with ownerReferences for the workload. - workload ->>kdoctor_controller: workload ready - service ->>kdoctor_controller: service ready - ingress ->>kdoctor_controller: ingress ready - kdoctor_controller ->>pod: task execution - kdoctor_controller ->>pod: timed report collection - pod ->>kdoctor_controller: task completion - kdoctor_controller ->>pod: report collection completed - pod ->>pod: report cleanup after 10 minutes of report collection. - kdoctor_controller ->>workload: runtime destruction time is reached, destroy workload. - workload ->>service: Destroy the service when it reaches runtime destruction time. - workload ->>ingress: reach runtime destruction time, destroy ingress - cr task ->>kdoctor_controller: cr task deleted - kdoctor_controller ->> workload: cr task deleted, workload deleted - workload ->> pod: workload deleted, pod deleted - workload ->>service: workload deleted, service deleted - workload ->>ingress: workload deleted, ingress deleted -``` +![lifecycle](../images/lifecycle.png) -* After a task is started, kdoctor-controller collects reports from the pods in the task at regular intervals, and after the task is completed, report collection is complete and no further report collection will be performed. -* After the task load finishes executing the task, the reports are collected by kdoctor-controller reports are automatically cleaned out of the load after 10 minutes by default. -* When a completed task CR is deleted, the report still exists in the kdoctor-controller report directory, but it cannot be viewed through the k8s aggregation api and needs to be viewed manually. -* When deleting an executing task CR, the task will be terminated, the resources generated during the creation of the CR will be deleted, and the collected reports will still be stored in the kdoctor-controller report catalog. \ No newline at end of file +* After a task is started, kdoctor-controller collects reports from the Pods in the task at regular interval. After the task is completed, report collection is complete and no further report collection will be performed. +* After the workload execute the task, the reports are collected by the kdoctor-controller. By default, after 10 minutes, the kdoctor-controller automatically cleans up the reports from the workload. +* When a completed task CR is deleted, the report still exists in the kdoctor-controller report directory. However, it cannot be viewed through the k8s aggregation api and needs to be viewed manually. +* When deleting an executing task CR, the task will be terminated, and the resources generated during the creation of the CR will be deleted, and the collected reports will still be stored in the kdoctor-controller report catalog. \ No newline at end of file diff --git a/docs/images/lifecycle-zh.png b/docs/images/lifecycle-zh.png new file mode 100644 index 0000000000000000000000000000000000000000..64ec3f4aa37cfa01afdf6690bcf165ecd70d6d61 GIT binary patch literal 221886 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

  • 0voZ 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%0Af%<_PWE!Dg^-k3C@ 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/images/lifecycle.png b/docs/images/lifecycle.png new file mode 100644 index 0000000000000000000000000000000000000000..129ef74884592ed2c26c96e779cf8d8212eae80b GIT binary patch literal 191570 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�s`F3+2E!Xw!mDxwWS69y% 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 Date: Fri, 10 Nov 2023 11:37:23 +0800 Subject: [PATCH 11/41] doc: update readme --- README-zh_CN.md | 14 +++++++------- README.md | 45 ++++++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/README-zh_CN.md b/README-zh_CN.md index 14c75a90..1a1f97fa 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -12,20 +12,20 @@ ## 介绍 -kdoctor 是一个基于主动式压力注入的 Kubernetes 数据面测试组件,对集群进行功能、性能的测试。通过调研和抽象了运维人员的常规运维需求,让网络、存储、应用等运维任务进行了云原生实现,基于 CRD的设计,能够对接观测性组件。 +kdoctor 是一个基于主动式压力注入的 Kubernetes 数据面测试组件,对集群进行功能、性能的测试。通过调研和抽象运维人员的常见需求,kdoctor 将网络、存储、应用等运维任务以云原生的方式实现。此外,还采用了基于 CRD 的设计,能够对接观测性组件。 **kdoctor 主要包含以下 3 个类型的任务:** -* [AppHttpHealthy](./docs/reference/apphttphealthy-zh_CN.md): 根据任务配置对集群内外指定访问地址,使用 HTTP、HTTPS 协议进行连通性检查,支持 PUT、GET、POST 等多种请求方式。 -* [NetReach](./docs/reference/netreach-zh_CN.md): 根据任务配置对集群内 Pod IP、ClusterIP、NodePort、Loadbalancer IP、Ingress IP, 甚至是 POD 多网卡、双栈IP进行连通性巡检。 -* [NetDns](./docs/reference/netdns-zh_CN.md): 根据任务配置,对集群内外的指定 DNS Server 进行连通性检测,支持 udp、tcp、tcp-tls 协议。 +* [AppHttpHealthy](./docs/reference/apphttphealthy-zh_CN.md): 根据任务配置,使用 HTTP、HTTPS 协议对集群内外指定访问地址进行连通性检查,支持 PUT、GET、POST 等多种请求方式。 +* [NetReach](./docs/reference/netreach-zh_CN.md): 根据任务配置对集群内 Pod IP、ClusterIP、NodePort、Loadbalancer IP、Ingress IP, 甚至是 Pod 多网卡、双栈 IP进行连通性巡检。 +* [NetDns](./docs/reference/netdns-zh_CN.md): 根据任务配置,对集群内外的指定 DNS Server 进行连通性检测,支持 UDP、TCP、TCP-TLS 协议。 **kdoctor 较传统的测试组件有哪些优势:** * 通过下发 CRD 配置巡检任务需求,使用者只需要关注巡检目标、巡检频率、发压参数以及期望巡检结果。 * 通过读取任务配置,以 Deployment 或 DaemonSet 的方式运行发压 agent,以达到多台发压机器的效果。 * 根据任务的 spec 配置,使用 default agent 或创建新的 agent 执行任务,以达到资源重复利用和任务资源隔离。 -* 绑定相对应的资源目标,如 ingress 、service,每一个 agent pod 根据任务配置相互访问绑定的资源,根据请求结果得出结论。 +* 绑定相对应的资源目标,如 ingress 、service,每一个 agent Pod 根据任务配置相互访问绑定的资源,根据请求结果得出结论。 * 发压 client 通过性能调优,大大降低了发压请求时的资源消耗。 -* 巡检报告通过日志、聚合 api 、文件落盘等方式输出。 +* 巡检报告通过日志、聚合 API、文件落盘等方式输出。 ## 架构 @@ -40,7 +40,7 @@ kdoctor 是一个基于主动式压力注入的 Kubernetes 数据面测试组件 ## 快速开始 **安装** -* [安装 kdoctor](./docs/usage/install-zh_CN.md) 或 [kind 快速开始](./docs/usage/install-zh_CN.md) +* 参考[安装 kdoctor](./docs/usage/install-zh_CN.md) 或 [kind 快速开始](./docs/usage/get-started-kind-zh_CN.md) **开始任务** * [开始任务 AppHttpHealthy](./docs/usage/apphttphealthy-zh_CN.md) diff --git a/README.md b/README.md index c9002a65..51911ec0 100644 --- a/README.md +++ b/README.md @@ -12,33 +12,44 @@ ## Introduction -kdoctor is a cloud native project of data plane test. Through the pressure injection, it realizes the active inspection for the function and performance of the cluster. +kdoctor is a Kubernetes data plane testing component that conducts functional and performance tests on clusters using proactive pressure injection. It addresses the operational needs of network, storage, and applications by adopting a cloud-native approach based on extensive research and abstraction. With its CRD design, kdoctor can seamlessly integrate with observability components. -For the traditional operation and maintenance , the status of clusters and applications is confirmed by collecting information such as metrics, logs, and application status, -which could be called passive inspection. However, in some special scenarios, this method may not meet the expected purpose, timeliness, or cluster range, -administrators need to manually inject some pressure into the cluster and checkout the cluster status, which could be called active inspection. -When the cluster scale is large, or the inspection frequency is high, or the inspection process is complicated, it is hard to implement manually. These scenarios include: +**kdoctor mainly offers three types of tasks:** +* [AppHttpHealthy](./docs/reference/apphttphealthy.md): according to the task configuration, perform connectivity checks using HTTP and HTTPS protocols on specified addresses within or outside the cluster, supporting various request methods such as PUT, GET, and POST. +* [NetReach](./docs/reference/netreach.md): conduct connectivity inspections on Pod IP, ClusterIP, NodePort, LoadBalancer IP, Ingress IP, and even Pods with multiple network interfaces or dual-stack IPs. +* [NetDns](./docs/reference/netdns.md): perform connectivity checks on designated DNS servers within or outside the cluster, supporting UDP, TCP, and TCP-TLS protocols. -* After deploying a large-scale cluster, administrators want to confirm the network connectivity between all nodes, to find out network failures on a certain - node, or occasional packet loss. In addition, there are many communication ways including POD IP, clusterIP, nodePort, loadbalancerIP, ingress, or even POD multiple network interface, dual-stack IP. +**Advantages of kdoctor over traditional testing components:** +* By configuring inspection tasks through CRDs, users only need to focus on the inspection targets, frequency, pressure parameters, and expected results. +* Pressure-injecting agents are dynamically run as Deployments or DaemonSets, achieving the effect of multiple pressure-injecting machines. +* The execution of tasks utilizes default agents or newly created agents based on the task's spec configurations, enabling resource reuse and task resource isolation. +* Agents are bound to corresponding resource targets such as ingress and service. Each agent Pod mutually accesses the bound resources according to the task configuration, deriving conclusions from the request results. +* Through performance optimization, the pressure-injecting client significantly reduces resource consumption during requests. +* Inspection reports can be generated through various means, including logging, aggregated APIs, and file storage. -* It is desired to make sure that PODs on all nodes can access the coredns service, or the resource configuration and the replica number of the coredns are enough to support expected access pressure. +## Architecture -* Disks are consumables and applications like etcd are sensitive to disk performance. In daily maintenance, administrators want to periodically confirm that local disks performance of all nodes are normal. +
    + Your Image Description +
    -* Actively inject pressure on a service like registry, mysql or api-server, to cooperate with BUG reproduce, or to confirm service performance +Components: +* kdoctor agent: kdoctor controller: a persistent Deployment responsible for CR monitoring, task creation, and task report aggregation. +* kdoctor agent: dynamically created on-demand as Deployments or DaemonSets to execute tasks. -kdoctor is a cloud native project of data plane test, which is derived from practices of the production operation and maintenance. Through the pressure injection, it realizes the active inspection for the function and performance of the cluster. kdoctor can be applied to scenarios: +## Quick Start -* inspection after creating new cluster, daily operation and maintenance. +**Install** +* Refer to [Install kdoctor](./docs/usage/install.md) 或 [kind Quick Start](./docs/usage//get-started-kind.md) -* E2E testing, bug reproduction, chaos testing. +**Task Get Started** +* [AppHttpHealthy Get Started](./docs/usage/apphttphealthy.md) +* [NetReach Get Started](./docs/usage/netreach.md) +* [NetDNS Get Started](./docs/usage/netdns.md) -## Architecture - -## Quick Start +## Contribution -## Feature +Refer to the [Contribution doc](./docs/develop/contributing.md). ## License From db288448ff1ebc021181be750cc9bcfa0b36a1f3 Mon Sep 17 00:00:00 2001 From: Jeanine-tw <76861242+Jeanine-tw@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:19:23 +0800 Subject: [PATCH 12/41] doc: update en reference --- docs/reference/apphttphealthy-zh_CN.md | 42 ++++----- docs/reference/apphttphealthy.md | 109 +++++++++++----------- docs/reference/arch.md | 8 +- docs/reference/kdoctor-agent.md | 20 ++-- docs/reference/kdoctor-controller.md | 45 +++++---- docs/reference/netdns-zh_CN.md | 40 ++++---- docs/reference/netdns.md | 124 +++++++++++++------------ docs/reference/netreach-zh_CN.md | 14 +-- docs/reference/netreach.md | 98 +++++++++---------- docs/reference/report.md | 21 ++--- 10 files changed, 261 insertions(+), 260 deletions(-) diff --git a/docs/reference/apphttphealthy-zh_CN.md b/docs/reference/apphttphealthy-zh_CN.md index 582f32ed..67669d5b 100644 --- a/docs/reference/apphttphealthy-zh_CN.md +++ b/docs/reference/apphttphealthy-zh_CN.md @@ -2,9 +2,9 @@ [**English**](./apphttphealthy.md) | **简体中文** -## 基本描述 +## 基本描述 -对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent pod 都会向指定的目标发送http请求,并获得成功率和平均延迟。它可以指定成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 +对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent Pod 都会向指定的目标发送 HTTP请求,并获得成功率和平均延迟。它可以指定成功条件来判断结果是否成功。并且,可以通过聚合 API 获取详细的报告。 ## AppHttpHealthy 示例 @@ -66,7 +66,7 @@ status: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | 任务执行agent配置 | [agentSpec](./apphttphealthy-zh_CN.md#AgentSpec) | 可选 | | | +| agentSpec | 任务执行 agent 配置 | [agentSpec](./apphttphealthy-zh_CN.md#AgentSpec) | 可选 | | | | schedule | 调度任务执行 | [schedule](./apphttphealthy-zh_CN.md#Schedule) | 可选 | | | | request | 对目标地址请求配置 | [request](./apphttphealthy-zh_CN.md#Request) | 可选 | | | | target | 请求目标设置 | [target](./apphttphealthy-zh_CN.md#Target) | 可选 | | | @@ -102,20 +102,20 @@ status: | perRequestTimeoutInMS | 每个请求的超时时间,不可大于 durationInSecond | int | 可选 | 大于等于 1 | 500 | | qps | 每一个 agent 每秒请求数量 | int | 可选 | 大于等于 1 | 5 | -> 注意:使用 agent 请求时,所有的 agent 都会向目标地址进行请求,因此实际 server 接收的 qps 等于 agent 数量 * 设置的qps。 +> 使用 agent 请求时,所有的 agent 都会向目标地址进行请求,因此实际 server 接收的 QPS 等于 agent 数量 * 设置的 QPS。 #### Target | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |------------------------|---------------------------------------------------------------------------------------------------------------------|--------|---------|---------------------------|-------| -| host | http 请求地址 | string | 必填 | | | -| method | http 请求方法 | string | 必填 | GET、POST、PUT、DELETE、CONNECT、OPTIONS、PATCH、HEAD | | -| bodyConfigmapName | http 请求 body 存放的 configmap 名称,[configmap 内容参考](./apphttphealthy-zh_CN.md#Body),若不需要 body 请求,忽略此字段 | string | 可选 | | | -| bodyConfigmapNamespace | http 请求 body 的 configmap 命名空间,如果 bodyConfigmapName 不为空,需要设置此字段 | string | 可选 | | | -| tlsSecretName | https 请求证书存放的 secret 名称,secret类型为 kubernetes.io/tls,[secret 内容参考](./apphttphealthy-zh_CN.md#Tls),若使用协议非 https 忽略此字段 | string | 可选 | | | -| tlsSecretNamespace | https 请求证书存放的 secret 命名空间,如果 tlsSecretName 字段不为空,需要设置此字段 | string | 可选 | | | -| header | http 请求头,数组形式,示例为 "Content-Type: application/json" | 元素为字符串的数组 | 可选 | | | -| http2 | 使用使用 http2 协议进行请求开关 | bool | 可选 | true,false | false | +| host | HTTP 请求地址 | string | 必填 | | | +| method | HTTP 请求方法 | string | 必填 | GET、POST、PUT、DELETE、CONNECT、OPTIONS、PATCH、HEAD | | +| bodyConfigmapName | HTTP 请求 body 存放的 configmap 名称,[configmap 内容参考](./apphttphealthy-zh_CN.md#Body),若不需要 body 请求,忽略此字段 | string | 可选 | | | +| bodyConfigmapNamespace | HTTP 请求 body 的 configmap 命名空间,如果 bodyConfigmapName 不为空,需要设置此字段 | string | 可选 | | | +| tlsSecretName | HTTPs 请求证书存放的 secret 名称,secret类型为 kubernetes.io/tls,[secret 内容参考](./apphttphealthy-zh_CN.md#Tls),若使用协议非 https 忽略此字段 | string | 可选 | | | +| tlsSecretNamespace | HTTPs 请求证书存放的 secret 命名空间,如果 tlsSecretName 字段不为空,需要设置此字段 | string | 可选 | | | +| header | HTTP 请求头,数组形式,示例为 "Content-Type: application/json" | 元素为字符串的数组 | 可选 | | | +| HTTP2 | 使用 HTTP2 协议进行请求开关 | bool | 可选 | true,false | false | | enableLatencyMetric | 统计演示分布,开启后会增加内存使用量 | bool | 可选 | true,false | false | #### Expect @@ -125,8 +125,8 @@ status: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |--------------------|---------------------------------|-------|-----|--------|------| | meanAccessDelayInMs | 平均延时,如果最终的结果 超过本值,任务会判定为失败 | int | 可选 | 大于等于 1 | 5000 | -| successRate | http请求成功率,如果最终的结果 小于本值,任务会判定为失败 | float | 可选 | 0-1 | 1 | -| statusCode | 期待的http返回状态码,如果最终的结果不等于本值,任务会判定为失败 | int | 可选 | 0-600 | 200 | +| successRate | HTTP 请求成功率,如果最终的结果 小于本值,任务会判定为失败 | float | 可选 | 0-1 | 1 | +| statusCode | 期待的 HTTP 返回状态码,如果最终的结果不等于本值,任务会判定为失败 | int | 可选 | 0-600 | 200 | #### Body @@ -145,9 +145,9 @@ data: ``` -#### Tls +#### TLS -https 请求使用证书时,若不填写 ca证书 则 不对证书安全进行校验,https 请求证书示例。 +HTTPs 请求使用证书时,若不填写 CA 证书则不对证书安全进行校验,HTTPs 请求证书示例。 ```yaml apiVersion: v1 @@ -163,7 +163,7 @@ type: kubernetes.io/tls ``` -### status +### Status | 字段 | 描述 | 结构 | 取值 | |--------------------|----------|------------------------------------------------|--------------| @@ -181,8 +181,8 @@ type: kubernetes.io/tls | status | 任务状态 | string | notstarted、ongoing、succeed、fail | | startTimeStamp | 本轮任务开始时间 | string | | | endTimeStamp | 本轮任务结束时间 | string | | -| duration | 本轮任执行时间 | string | | +| duration | 本轮任务执行时间 | string | | | deadLineTimeStamp | 本轮任务 deadline | string | | -| failedAgentNodeList | 任务失败的 agent | 元素为string的数组 | | -| succeedAgentNodeList | 任务成功的 agent | 元素为string的数组 | | -| notReportAgentNodeList | 没有上传任务报告的 agent | 元素为string的数组 | | +| failedAgentNodeList | 任务失败的 agent | 元素为 string 的数组 | | +| succeedAgentNodeList | 任务成功的 agent | 元素为 string 的数组 | | +| notReportAgentNodeList | 没有上传任务报告的 agent | 元素为 string 的数组 | | diff --git a/docs/reference/apphttphealthy.md b/docs/reference/apphttphealthy.md index 1eab5a3e..e43a9087 100644 --- a/docs/reference/apphttphealthy.md +++ b/docs/reference/apphttphealthy.md @@ -4,7 +4,7 @@ ## Basic description -For this kind of task, kdoctor-controller generates the corresponding [agent](../concepts/runtime.md) and other resources. Each agent pod sends an http request to the specified target and gets the success rate and average latency. It can specify the success condition to determine whether the result is successful or not. And, detailed reports can be obtained through the aggregation API. +For this kind of task, kdoctor-controller generates the corresponding [agent](../concepts/runtime.md) and other resources. Each agent Pod sends an HTTP request to the specified target and gets the success rate and average latency. It can specify the success condition to determine whether the result is successful or not. And, detailed reports can be obtained through the aggregation API. ## AppHttpHealthy Example @@ -60,74 +60,75 @@ status: | Fields | Descriptions | Structures | Validations | |-----|---------------|--------|-----| -| name | Name of the AppHttpHealthy resource | string | mandatory | +| Name | Name of the AppHttpHealthy Resource | String | Required | ### Spec -| fields | description | structure | validation | values | default | +| Fields | Description | Structure | Validation | Values | Default | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | task execution agent configuration | [agentSpec](./apphttphealthy.md#agentspec) | optional | | | -| schedule | Scheduling Task Execution | [schedule](./apphttphealthy.md#schedule) | optional | | | -| request | Request configuration for a destination address | [request](./apphttphealthy.md#request) | optional | | | | -| target | Request target settings | [target](./apphttphealthy.md#target) | optional | | | | -| expect | Task success condition judgment | [expect](./apphttphealthy.md#expect) | optional | | | +| agentSpec | Task Execution Agent Configuration | [agentSpec](./apphttphealthy.md#agentspec) | Optional | | | +| Schedule | Scheduling Task Execution | [schedule](./apphttphealthy.md#schedule) | Optional | | | +| Request | Request Configuration for a Destination Address | [request](./apphttphealthy.md#request) | Optional | | | | +| Target | Request Target Settings | [target](./apphttphealthy.md#target) | Optional | | | | +| Expect | Task Success Condition Judgment | [expect](./apphttphealthy.md#expect) | Optional | | | #### AgentSpec | Fields | Description | Structure | Validation | Values | Default | |-------------------------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----|----------------------|-------------------------------| -| annotation | annotation of agent workload | map[string]string | optional | | | | -| kind | type of agent workload | string | optional | Deployment, DaemonSet | DaemonSet | -| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | optional | greater than or equal to 0 | 0 | -| affinity | agent workload affinity | labelSelector | optional | | | -| env | agent workload environment variable | env | optional | | | | hostNetwork | agent -| hostNetwork | agent Whether or not the workload uses the host network | bool | optional | true, false | false | -| resources | agent workload resource usage configuration | resources | optional | | limit cpu:1000m,memory:1024Mi | -| terminationGracePeriodMinutes | agent How many minutes after a workload completes a task before it terminates | int | optional | greater than or equal to 0 | 60 | +|Annotation |Annotation of Agent Workload | Map[string]String | Optional | | | | +| Kind | Type of Agent Workload | String | Optional | Deployment, DaemonSet | DaemonSet | +| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | Optional |Greater than or Equal to 0 | 0 | +|Affinity | Agent Workload Affinity | labelSelector | Optional | | | +| env | Agent Workload Environment Variable | env | Optional | | | | hostNetwork | Agent +| hostNetwork | Whether or not the agent workload uses the host network | Bool | Optional |True, false |False | +| Resources | Agent Workload Resource Usage Configuration | Resources | Optional | | Limit CPU:1000m,Memory:1024Mi | +| terminationGracePeriodMinutes | the minutes after a agent workload completes a task before it terminates | int | Optional | Greater than or equal to 0 | 60 | #### Schedule | Fields | Description | Structure | Validation | Values | Defaults | |--------------------|---------------------------------------|--------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------| -| roundNumber | task execution rounds | int | optional | greater than or equal to -1, -1 means permanent, greater than 0 means rounds will be executed | 1 | schedule | task execution time, execution time should be less than roundTimeoutMinute | string | optional | Support linux crontab and interval method.
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: written in "M N" format, M takes the value of a number, which means how many minutes after opening the task, N takes the value of a number, indicating how many minutes between each round of task execution, for example, "0 1" means start the task immediately, each round of task interval 1min | "0 1" | roundTimeoutMininute -| roundTimeoutMinute | Task timeout, need to be greater than durationInSecond and task execution time | int | optional | greater than or equal to 1 | 60 | +| roundNumber | Task Execution Rounds | int |Optional |A value greater than or equal to -1 indicates indefinite execution, with -1 representing permanent execution. A value greater than 0 represents the number of rounds to be executed | 1 | Schedule | Task execution time which should be less than roundTimeoutMinute | String | Optional | Support linux crontab and interval method
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: writing format "M N". M is a number that indicates how many minutes after the task is started; N is a number that indicates how many minutes between each round of tasks. For example, "0 1" means start the task immediately, 1min between each round of tasks.| "0 1" | +| roundTimeoutMinute | Task timeout which needs to be greater than durationInSecond and task execution time | int | optional | greater than or equal to 1 | 60 | #### Request | Fields | Description | Structure | Validation | Values | Defaults | |------------------------|---------------------------------------|--------|-----|---------------|---------------| -| durationInSecond | Duration of request send pressure for each round of tasks less than roundTimeoutMinute | int | optional | greater than or equal to 1 | 2 | -| perRequestTimeoutInMS | timeout per request, not greater than durationInSecond | int | optional | greater than or equal to 1 | 500 | -| qps | Number of requests per second per agent | int | optional | greater than or equal to 1 | 5 | +| durationInSecond | Duration of request send pressure for each round of tasks which is less than roundTimeoutMinute | int |Optional |Greater than or equal to 1 | 2 | +| perRequestTimeoutInMS |Timeout per request, not greater than durationInSecond | int |Optional | Greater than or equal to 1 | 500 | +| QPS | Number of requests per second per agent | int | Optional | Greater than or equal to 1 | 5 | + +> When using agent requests, all agents will make requests to the destination address, so the actual QPS received by the server is equal to the number of agents multiplied by the set QPS. -> Note: When using agent requests, all agents will make requests to the destination address, so the actual qps received by the server is equal to the number of agents * the set qps. #### Target | Fields | Description | Structures | Validation | Values | Defaults | |------------------------|---------------------------------------------------------------------------------------------------------------------|--------|---------|---------------------------|-------| -| host | http request address | string | | Required | | | | | -| method | http request method | string | mandatory | GET, POST, PUT, DELETE, CONNECT, OPTIONS, PATCH, HEAD | | bodyConfigmap -| bodyConfigmapName | The name of the configmap stored in the body of the http request, [configmap content reference](./apphttphealthy.md#body) -| bodyConfigmapNamespace | http request body's configmap namespace, if bodyConfigmapName is not empty, need to set this field | string | optional | | | | | | | | | tlsSecretName -| tlsSecretName | The name of the secret where the https request certificate is stored, with a secret of type kubernetes.io/tls, [secret content reference](./apphttphealthy.md#tls), ignore this field if using a protocol other than https | string | | optional | | | | | tlsSecretName | tlsSecretName -| tlsSecretNamespace | The secret namespace where the https request certificate is stored, if the tlsSecretName field is not null, you need to set this field | string | optional | | | | -| header | http request header, as an array, for example "Content-Type: application/json" | elements are an array of strings | optional | | | | -| http2 | Use the request using http2 protocol switch | bool | optional | true,false | false | -| enableLatencyMetric | Statistical demo distribution, turn it on to increase memory usage | bool | optional | true,false | false | +| Host | HTTP Request Address |String | | Required | | | | | +|Method | HTTP Request Method | String | Required | GET, POST, PUT, DELETE, CONNECT, OPTIONS, PATCH, HEAD | | +| bodyConfigmapName | The name of the configmap stored in the body of the HTTP request. Refer to [configmap](./apphttphealthy.md#body). If you don't need a body request, ignore this field.| String| Optional| | | +| bodyConfigmapNamespace | HTTP request body's configmap namespace. If bodyConfigmapName is not empty, you need to set this field | string | optional | | | +| tlsSecretName | The name of the secret where the HTTPs request certificate is stored, with a secret of type kubernetes.io/tls. Refer to [secret](./apphttphealthy.md#tls). Ignore this field if using a protocol other than HTTPs |String | optional | | | +| tlsSecretNamespace | The secret namespace where the HTTPs request certificate is stored. If the tlsSecretName field is not null, you need to set this field |String | Optional | | | +| header | HTTP request header, in the form of an array, for example "Content-Type: application/json" | Elements are an array of strings |Optional | | | +| HTTP2 | Use the request HTTP2 protocol switch | bool | Optional | True,false | False | +| enableLatencyMetric | Statistics demo distribution, which increases memory usage when turned on | Bool | Optional | True,false | False | #### Expect -Task success condition, if the task result does not meet the expected condition, the task fails. +Task success condition. If the task result does not meet the expected condition, the task fails. | Fields | Description | Structures | Validation | Values | Defaults | |--------------------|---------------------------------|-------|-----|--------|------| -| meanAccessDelayInMs | meanAccessDelayInMs | The average delay, if the final result exceeds this value, the task will be judged as failed | int | optional | greater than or equal to 1 | 5000 | -| successRate | The success rate of the http request, if the final result is less than this value, the task will fail | float | optional | 0-1 | 1 | statusCode | The status code to expect. -| statusCode | The status code to expect from the http request, if the final result is not equal to this value, the task will be judged as failed | int | optional | 0-600 | 200 | +| meanAccessDelayInMs | The average delay. If the final result exceeds this value, the task will be judged as failed | int | Optional | Greater than or equal to 1 | 5000 | +| successRate | Success rate of the HTTP request. If the final result is less than this value, the task will fail | Float | Optional | 0-1 | 1 | +| statusCode | The expected HTTP return status code. If the final result is not equal to this value, the task will be determined to have failed | int | Optional | 0-600 | 200 | #### Body -Carrying a body request, example of how to write a body +Carry a body request. Example of how to write a body ```yaml apiVersion: v1 @@ -140,11 +141,11 @@ data: kdoctor-test-body-5656-89207258 namespace: kdoctor test2: test2 ``` -#### Tls +#### TLS -If you don't fill in the ca certificate when using certificate for https request, the certificate security will not be verified, https request certificate example. +If you don't fill in the CA certificate when using certificate for HTTPs request, the certificate security will not be verified, HTTPs request certificate example: -``` yaml +```yaml apiVersion: v1 data: ca.crt: xxxxxxx ca.crt: xxxxxxxxxxxbase64 @@ -158,24 +159,26 @@ type: kubernetes.io/tls ``` -### status +### Status -| fields | description | structures | values | +| Fields | Description | Structures | Values | |--------------------|----------|------------------------------------------------|--------------| -| doneRound | number of completed task rounds | int | | | -| expectedRound | number of rounds expected to be performed | int | | | -| finish | Whether the task is complete or not | bool | true, false | -| lastRoundStatus | lastRoundStatus | string | notstarted, on-going, succeed, fail | -| history | Task history | Element is [history](./apphttphealthy.md#history) array | | +| doneRound | Number of completed task rounds | int | | +| expectedRound | Number of rounds expected to be performed | int | | +| Finish | Whether the task is complete or not |Bool | True, false | +| lastRoundStatus | lastRoundStatus | String |Notstarted, on-going, succeed, fail | +| History | Task History | Element is [history](./apphttphealthy.md#history) array | | #### History | Fields | Description | Structure | Values | |----------------------------------|-----------------|--------------|---------------------------| -| roundNumber | Task round number | int | | -| status | Task status | string | notstarted, ongoing, succeed, fail | -| startTimeStamp | startTimeStamp | startTimeStamp | string | | -| startTimeStamp | startTimeStamp | startTimeStamp | endTimeStamp | endTimeStamp | endTimeStamp | string | -| deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadline | string | | -| failedAgentNodeList | failedAgentNodeList | Array of failed agents | string | -| notReportAgentNodeList | agent who did not upload a task report | array of elements as string | | notReportAgentNodeList | agent who failed to upload a task report | array of elements as string| +| roundNumber | Task Round Number | int | | +| Status | Task Status | String | Notstarted, ongoing, succeed, fail | +| startTimeStamp | Start of the current round of tasks | String | | +| endTimeStamp | End of the current round of tasks | string | | +| duration |Execution time of the current round of tasks |string | | +| deadLineTimeStamp | Deadline of the current round of tasks | string | | +| failedAgentNodeList | Agent whose tasks failed |Array of elements as string | | +| succeedAgentNodeList |Agent whose task succeeded | Array of elements as string | | +| notReportAgentNodeList |Agent who did not upload a task report | Array of elements as string | | diff --git a/docs/reference/arch.md b/docs/reference/arch.md index 0056d8f6..451fffed 100644 --- a/docs/reference/arch.md +++ b/docs/reference/arch.md @@ -1,9 +1,9 @@ -# architecture +# Architecture -The kdoctor is aimed to test the cluster and generate task reports to check whether the cluster is healthy. +The kdoctor aims to test clusters and generate task reports to check whether the cluster is healthy. It consists of controller deployment and agent daemonset. -* the controller schedules the task, update and summarize the task result, and aggerate all reports. +* The controller schedules the task, update and summarize the task result, and aggregate all reports. -* the agent implement tasks +* The agent implement tasks. diff --git a/docs/reference/kdoctor-agent.md b/docs/reference/kdoctor-agent.md index c29f9a31..b89bb115 100644 --- a/docs/reference/kdoctor-agent.md +++ b/docs/reference/kdoctor-agent.md @@ -2,22 +2,22 @@ This page describes CLI options and ENV of kdoctor-agent. -## kdoctor-agent daemon +## kdoctor-agent Daemon Run the kdoctor agent daemon. ### Options -| options | type | default | description | +| Options | Type | Default | Description | |---------------------|--------|--------------------------|--------------------------------------------------------------------------------------------------------| -| --config-dir | string | /tmp/config-map/conf.yml | config file path. | -| --app-mode | bool | false | agent running mode ,when using app mode, the agent only provides an HTTP and HTTPS server for testing. | -| --tls-insecure | bool | true | The HTTPS server skips TLS authentication. | -| --default-agent | bool | false | The default agent performs tasks. | -| --tls-ca-cert | string | /etc/tls/ca.crt | The CA certificate path, which is used by the agent to generate the signing certificate. | -| --tls-ca-key | string | /etc/tls/ca.key | The CA key path, which is used by the agent to generate the signing certificate. | -| --task-kind | string | "" | The kind of task. values AppHttpHealthy、NetReach and Netdns. | -| --task-name | string | "" | The name of task. | +| --config-dir | String | /tmp/config-map/conf.yml | Config file path | +| --app-mode | bool | false | Agent running mode. When using app mode, the agent only provides an HTTP and HTTPS server for testing. | +| --tls-insecure | Bool | True | The HTTPS server skips TLS authentication. | +| --default-agent | Bool | False | The default agent performs tasks. | +| --tls-ca-cert | String | /etc/tls/ca.crt | The CA certificate path, which is used by the agent to generate the signing certificate. | +| --tls-ca-key | String | /etc/tls/ca.key | The CA key path, which is used by the agent to generate the signing certificate. | +| --task-kind | String | "" | The kind of task. values AppHttpHealthy、NetReach and Netdns. | +| --task-name | String | "" | The name of task. | | --service-ipv4-name | string | "" | The ipv4 service name of the task workload. | | --service-ipv6-name | string | "" | The ipv6 service name of the task workload. | diff --git a/docs/reference/kdoctor-controller.md b/docs/reference/kdoctor-controller.md index 5d2037a4..9a7394b6 100644 --- a/docs/reference/kdoctor-controller.md +++ b/docs/reference/kdoctor-controller.md @@ -2,48 +2,45 @@ This page describes CLI options and ENV of kdoctor-controller. -## kdoctor-controller daemon +## kdoctor-controller Daemon Run the kdoctor controller daemon. ### Options -| options | type | default | description | +|Options | Type | Default | Description | |----------------------------------|--------|--------------------------------------------|----------------------------------------------------------------------| -| --config-dir | string | /tmp/config-map/conf.yml | config file path. | -| --tls-ca-cert | string | /etc/tls/ca.crt | The CA certificate path, The CA is used to validate the certificate. | +| --config-dir | String | /tmp/config-map/conf.yml | Config file path. | +| --tls-ca-cert | string | /etc/tls/ca.crt | The CA certificate path. The CA is used to validate the certificate. | | --tls-server-cert | string | /etc/tls/tls.crt | The server tls cert path. | | --tls-server-key | string | /etc/tls/tls.key | The server tls key path. | | --configmap-deployment-template | string | /tmp/configmap-app-template/deployment.yml | The configmap deployment template file path. | | --configmap-daemonset-template | string | /tmp/configmap-app-template/daemonset.yml | The configmap daemonset template file path. | -| --configmap-pod-template | string | /tmp/configmap-app-template/pod.yml | The configmap pod template file path. | +| --configmap-pod-template | string | /tmp/configmap-app-template/pod.yml | The configmap Pod template file path. | | --configmap-service-template | string | /tmp/configmap-app-template/service.yml | The configmap service template file path. | | --configmap-ingress-template | string | /tmp/configmap-app-template/ingress.yml | The configmap ingress template file path. | ### ENV -| env | default | description | +| Env | Default | Description | |---------------------------------------------|---------------|------------------------------------------------------------------------------------| -| ENV_LOG_LEVEL | info | Log level, optional values are "debug", "info", "warn", "error", "fatal", "panic". | -| ENV_ENABLED_METRIC | false | Enable/disable metrics. | +| ENV_LOG_LEVEL | Info | Log level.Optional values are "debug", "info", "warn", "error", "fatal", "panic". | +| ENV_ENABLED_METRIC |False | Enable/disable metrics. | | ENV_METRIC_HTTP_PORT | 5711 | Metric HTTP server port. | | ENV_HTTP_PORT | 80 | kdoctor-controller backend HTTP server port. | -| ENV_ENABLE_AGGREGATE_AGENT_REPORT | false | enable aggregate report | -| ENV_CLEAN_AGED_REPORT_INTERVAL_IN_MINUTE | 10 | clean aggregate report interval in minute | -| ENV_COLLECT_AGENT_REPORT_INTERVAL_IN_SECOND | 600 | collect agent report interval time | -| ENV_CONTROLLER_REPORT_AGE_IN_DAY | 30 | controller report age in ady | -| ENV_AGENT_REPORT_STORAGE_PATH | /report | aggregate report storage path | -| ENV_CONTROLLER_REPORT_STORAGE_PATH | /report | controller report storage path | +| ENV_ENABLE_AGGREGATE_AGENT_REPORT |False |Enable aggregate report | +| ENV_CLEAN_AGED_REPORT_INTERVAL_IN_MINUTE | 10 | Clean aggregate report interval in minutes | +| ENV_COLLECT_AGENT_REPORT_INTERVAL_IN_SECOND | 600 | Collect agent report interval time | +| ENV_CONTROLLER_REPORT_AGE_IN_DAY | 30 |Controller report age in ady | +| ENV_AGENT_REPORT_STORAGE_PATH | /report | Aggregate report storage path | +| ENV_CONTROLLER_REPORT_STORAGE_PATH | /report | Controller report storage path | | ENV_GOPS_LISTEN_PORT | 5724 | Gops port | -| ENV_WEBHOOK_PORT | 5722 | controller webhook port | +| ENV_WEBHOOK_PORT | 5722 | Controller webhook port | | ENV_PYROSCOPE_PUSH_SERVER_ADDRESS | "" | pyroscope addr | -| ENV_POD_NAME | "" | controller pod name | -| ENV_POD_NAMESPACE | "" | controller pod namespace | +| ENV_POD_NAME | "" | Controller Pod name | +| ENV_POD_NAMESPACE | "" | Controller Pod namespace | | ENV_GOLANG_MAXPROCS | 8 | golang runtime max procs | -| ENV_DEFAULT_AGENT_NAME | kdoctor-agent | default agent name | -| ENV_DEFAULT_AGENT_TYPE | daemonset | default agent type | -| ENV_DEFAULT_AGENT_SERVICE_V4_NAME | "" | default agent server ipv4 name | -| ENV_DEFAULT_AGENT_SERVICE_V6_NAME | "" | default agent server ipv6 name | - - - +| ENV_DEFAULT_AGENT_NAME | kdoctor-agent | Default agent name | +| ENV_DEFAULT_AGENT_TYPE | Daemonset | Default agent type | +| ENV_DEFAULT_AGENT_SERVICE_V4_NAME | "" | Default agent server IPv4 name | +| ENV_DEFAULT_AGENT_SERVICE_V6_NAME | "" | Default agent server IPv6 name | diff --git a/docs/reference/netdns-zh_CN.md b/docs/reference/netdns-zh_CN.md index 8d15b168..c552d17a 100644 --- a/docs/reference/netdns-zh_CN.md +++ b/docs/reference/netdns-zh_CN.md @@ -4,13 +4,13 @@ ## 基本描述 -对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent pod 都会向指定的目标发送 Dns 请求,并获得成功率和平均延迟。 它可以指定成功条件来告知结果成功或失败。 +对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent Pod 都会向指定的目标发送 DNS 请求,并获得成功率和平均延迟。它可以指定成功条件来告知结果成功或失败。 ## netdns 示例 -### 集群 Dns Server 检查 +### 集群 DNS Server 检查 -对集群内的 dns server(coredns)发送对应请求,获取集群内 dns server 性能状态。 +对集群内的 DNS server(CoreDNS)发送对应请求,获取集群内 DNS server 性能状态。 ```yaml apiVersion: kdoctor.io/v1beta1 @@ -62,9 +62,9 @@ status: lastRoundStatus: succeed ``` -### 指定 dns server 检查 +### 指定 DNS server 检查 -对集群外部的 dns server 发送对应请求,获取集群外部 dns server 性能状态。 +对集群外部的 DNS server 发送对应请求,获取集群外部 DNS server 性能状态。 ```Yaml apiVersion: kdoctor.io/v1beta1 @@ -104,7 +104,7 @@ spec: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | 任务执行agent配置 | [agentSpec](./netdns-zh_CN.md#AgentSpec) | 可选 | | | +| agentSpec | 任务执行agent 配置 | [agentSpec](./netdns-zh_CN.md#AgentSpec) | 可选 | | | | schedule | 调度任务执行 | [schedule](./netdns-zh_CN.md#Schedule) | 可选 | | | | request | 对目标地址请求配置 | [request](./netdns-zh_CN.md#Request) | 可选 | | | | target | 请求目标设置 | [target](./netdns-zh_CN.md#Target) | 可选 | | | @@ -129,7 +129,7 @@ spec: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |--------------------|---------------------------------------|--------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| | roundNumber | 任务执行轮数 | int | 可选 | 大于等于-1,为 -1 时表示永久执行,大于 0 表示将要执行的轮数 | 1 | -| schedule | 任务执行时间, 执行时间应小于roundTimeoutMinute | string | 可选 | 支持 linux crontab 与间隔法两种写法
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * 表示每分钟执行一次
    间隔法:书写格式为 “M N” ,M取值为一个数字,表示多少分钟之后开启任务,N取值为一个数字,表示每一轮任务的间隔多少分钟执行,例如 “0 1” 表示立即开始任务,每轮任务间隔1min | "0 60" | +| schedule | 任务执行时间, 执行时间应小于roundTimeoutMinute | string | 可选 | 支持 linux crontab 与间隔法两种写法
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * 表示每分钟执行一次
    间隔法:书写格式为 “M N” ,M 取值为一个数字,表示多少分钟之后开启任务,N取值为一个数字,表示每一轮任务的间隔多少分钟执行,例如 “0 1” 表示立即开始任务,每轮任务间隔 1min | "0 60" | | roundTimeoutMinute | 任务超时时间,需要大于 durationInSecond 和 任务执行时间 | int | 可选 | 大于等于 1 | 60 | #### Request @@ -139,17 +139,17 @@ spec: | durationInSecond | 每轮任务的请求发压的持续时间,小于roundTimeoutMinute | int | 可选 | 大于等于 1 | 2 | | perRequestTimeoutInMS | 每个请求的超时时间,不可大于 durationInSecond | int | 可选 | 大于等于 1 | 500 | | qps | 每一个 agent 每秒请求数量 | int | 可选 | 大于等于 1 | 5 | -| protocol | 请求协议 | string | 可选 | udp、tcp、tcp-tls | udp | -| domain | dns 请求解析的域名 | string | 可选 | | kubernetes.default.svc.cluster.local | +| protocol | 请求协议 | string | 可选 | UDP、TCP、TCP-TLS | UDP | +| domain | DNS 请求解析的域名 | string | 可选 | | kubernetes.default.svc.cluster.local | -> 注意:使用 agent 请求时,所有的 agent 都会向目标地址进行请求,因此实际 server 接收的 qps 等于 agent 数量 * 设置的qps。 +> 注意:使用 agent 请求时,所有的 agent 都会向目标地址进行请求,因此实际 server 接收的 QPS 等于 agent 数量 * 设置的 QPS。 #### Target | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |--------------------|-------------------------|----------------------------------------------|-----|------------|-------| -| targetUser | 对用户自定义的 dns server 进行 dns 请求| [targetUser](./netdns-zh_CN.md#TargetUser) | 可选 | | true | -| targetDns | 对集群的 dns server(coredns)进行 dns 请求 | [targetDns](./netdns-zh_CN.md#TargetDns) | 可选 | | true | +| targetUser | 对用户自定义的 DNS server 进行 DNS 请求| [targetUser](./netdns-zh_CN.md#TargetUser) | 可选 | | true | +| targetDns | 对集群的 DNS server(CoreDNS)进行 DNS 请求 | [targetDns](./netdns-zh_CN.md#TargetDns) | 可选 | | true | | enableLatencyMetric | 统计演示分布,开启后会增加内存使用量 | bool | 可选 | true,false | false | #### Expect @@ -163,24 +163,24 @@ spec: #### TargetUser -测试用户自定义 dns server +测试用户自定义 DNS server | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |-------------|---------------|--------|-----|---------|---| -| server | dns server 地址 | string | 必填 | | | -| port | dns server 端口 | int | 必填 | 1-65535 | | +| server | DNS server 地址 | string | 必填 | | | +| port | DNS server 端口 | int | 必填 | 1-65535 | | #### TargetDns -测试集群内 dns server +测试集群内 DNS server | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |----------|--------------------------|--------|-----|-----------|-------| -| testIPv4 | 测试 ipv4 地址 请求 A 记录 | bool | 可选 | true,false | true | -| testIPv6 | 测试 ipv6 地址 请求 AAAA 记录 | bool | 可选 | true,false | false | -| serviceName | 集群 dns server service 地址 | string | 可选 | | | -| serviceNamespace | 集群 dns server service 端口 | string | 可选 | | | +| testIPv4 | 测试 IPv4 地址 请求 A 记录 | bool | 可选 | true,false | true | +| testIPv6 | 测试 IPv6 地址 请求 AAAA 记录 | bool | 可选 | true,false | false | +| serviceName | 集群 DNS server service 地址 | string | 可选 | | | +| serviceNamespace | 集群 DNS server service 端口 | string | 可选 | | | ### status diff --git a/docs/reference/netdns.md b/docs/reference/netdns.md index 1c6ffe75..16532623 100644 --- a/docs/reference/netdns.md +++ b/docs/reference/netdns.md @@ -2,15 +2,15 @@ [**简体中文**](./netdns-zh_CN.md) | **English** -## Basic description +## Basic Description -For this kind of task, kdoctor-controller generates the corresponding [agent](../concepts/runtime.md) and other resources. Each agent pod sends a Dns request to the specified target and gets the success rate and average latency. It can specify a success condition to inform the result of success or failure. +For this kind of task, kdoctor-controller generates the corresponding [agent](../concepts/runtime.md) and other resources. Each agent Pod sends a DNS request to the specified target and gets the success rate and average latency. It can specify a success condition to inform the result of success or failure. ## netdns Example -### Cluster Dns Server Check +### Cluster DNS Server Check -Sends a corresponding request to a dns server (coredns) in a cluster to get the performance status of the dns server in the cluster. +Send a corresponding request to a DNS server (CoreDNS) in a cluster to get the performance status of the DNS server in the cluster. ```yaml apiVersion: kdoctor.io/v1beta1 @@ -62,9 +62,9 @@ status: lastRoundStatus: succeed ``` -### Specify dns server to check +### Specify DNS Server Check -Send a corresponding request to a dns server outside the cluster to get the performance status of the dns server outside the cluster. +Send a corresponding request to a DNS server outside the cluster to get its performance status. ```Yaml apiVersion: kdoctor.io/v1beta1 @@ -91,111 +91,113 @@ spec: port: 53 server: 172.41.54.83 ``` -## netdns definition + +## netdns Definition ### Metadata -| fields | description | structure | validation | +|Fields | Description | Structure | Validation | |-----|---------------|--------|-----| -| name | Name of the netdns resource | string | required | +|Name | Name of the netdns Resource | String | Required | ### Spec -| fields | description | structure | validation | take values | default | +| Fields | Description | Structure | Validation | Take Values | Default | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | task execution agent configuration | [agentSpec](./apphttphealthy.md#agentspec) | optional | | | -| schedule | Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | optional | | | -| request | Request configuration for destination address | [request](./netdns.md#request) | Optional | | | -| target | Request target settings | [target](./apphttphealthy.md#target) | Optional | | | -| expect | Task success condition judgment | [expect](./apphttphealthy.md#expect) | Optional | | | +| agentSpec | Task Execution Agent Configuration | [agentSpec](./apphttphealthy.md#agentspec) | Optional | | | +| Schedule | Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | Optional | | | +|Request | Request Configuration for Destination Address | [request](./netdns.md#request) | Optional | | | +| Target | Request Target Settings | [Target](./apphttphealthy.md#target) | Optional | | | +| Expect | Task Success Condition Judgment | [expect](./apphttphealthy.md#expect) | Optional | | | #### AgentSpec | Fields | Description | Structure | Validation | Values | Default | |-------------------------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----|----------------------|-------------------------------| -| annotation | annotation of agent workload | map[string]string | optional | | | | -| kind | type of agent workload | string | optional | Deployment, DaemonSet | DaemonSet | -| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | optional | greater than or equal to 0 | 0 | -| affinity | agent workload affinity | labelSelector | optional | | | -| env | agent workload environment variable | env | optional | | | | hostNetwork | agent -| hostNetwork | agent Whether or not the workload uses the host network | bool | optional | true, false | false | -| resources | agent workload resource usage configuration | resources | optional | | limit cpu:1000m,memory:1024Mi | -| terminationGracePeriodMinutes | agent How many minutes after a workload completes a task before it terminates | int | optional | greater than or equal to 0 | 60 | +|Annotation | Annotation of Agent Workload |Map[string]String | Optional | | | +| kind |Type of Agent Workload | String | Optional | Deployment, DaemonSet | DaemonSet | +| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | Optional | Greater than or equal to 0 | 0 | +|Affinity | Agent Workload Affinity | labelSelector | Optional | | | +| env | Agent Workload Environment Variable | env |Optional | | | +| hostNetwork | Whether or not the agent workload uses the host network | Bool | Optional | True, false | False | +| Resources | Agent Workload Resource Usage Configuration | Resources | Optional | | Limit cpu: 1000m,Memory:1024Mi | +| terminationGracePeriodMinutes | the minutes after a agent workload completes a task before it terminates | int | Optional |Greater than or equal to 0 | 60 | #### Schedule | Fields | Description | Structure | Validation | Values | Defaults | |--------------------|---------------------------------------|--------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| -| roundNumber | task execution rounds | int | optional | greater than or equal to -1, -1 means permanent, greater than 0 means rounds will be executed | 1 | schedule | task execution time, execution time should be less than roundTimeoutMinute | string | optional | Support linux crontab and interval method.
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: written in "M N" format, M takes the value of a number, indicating how many minutes after opening the task, N takes the value of a number, indicating how many minutes between each round of task execution, for example, "0 1" means start the task immediately, each round of task interval 1min | "0 60" | roundTimeoutMininute -| roundTimeoutMinute | Task timeout, needs to be greater than durationInSecond and task execution time | int | optional | greater than or equal to 1 | 60 | +| roundNumber |Task Execution Rounds | int | Optional | A value greater than or equal to -1 indicates indefinite execution, with -1 representing permanent execution. A value greater than 0 represents the number of rounds to be executed | 1 | Schedule | Task execution time which should be less than roundTimeoutMinute | String | Optional | Support linux crontab and interval method
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: writing format "M N". M is a number that indicates how many minutes after the task is started; N is a number that indicates how many minutes between each round of tasks. For example, "0 1" means start the task immediately, 1min between each round of tasks. | "0 60" | +| roundTimeoutMinute | Task timeout which needs to be greater than durationInSecond and task execution time | int | Optional | Greater than or equal to 1 | 60 | #### Request | Fields | Description | Structure | Validation | Values | Defaults | |------------------------|---------------------------------------|--------|-----|---------------|---------------| -| durationInSecond | Duration of request send pressure for each round of tasks less than roundTimeoutMinute | int | optional | greater than or equal to 1 | 2 | -| perRequestTimeoutInMS | timeout per request, not greater than durationInSecond | int | optional | greater than or equal to 1 | 500 | -| qps | requests per second per agent | int | optional | greater than or equal to 1 | 5 | protocol | request protocol -| protocol | request protocol | string | optional | udp, tcp, tcp-tls | udp | domain | dns Requests for -| domain | dns The domain name for which the request is to be resolved | string | optional | | kubernetes.default.svc.cluster.local | | kubernetes.default.svc. +| durationInSecond | Duration of request send pressure for each round of tasks which is less than roundTimeoutMinute | int |Optional | Greater than or equal to 1 | 2 | +| perRequestTimeoutInMS | Timeout per request, not greater than durationInSecond | int |Optional | Greater than or equal to 1 | 500 | +| QPS | Requests per second per agent | int | Optional | Greater than or equal to 1 | 5 | Protocol | Request Protocol +| Protocol | Request Protocol | String | Optional | UDP, TCP, TCP-TLS | UDP | Domain | The domain for which the DNS request to resolve | string | Optional | | kubernetes.default.svc.cluster.local | -> Note: When using agent requests, all agents make requests to the destination address, so the actual qps received by the server is equal to the number of agents * the set qps. +> When using agent requests, all agents will make requests to the destination address, so the actual QPS received by the server is equal to the number of agents multiplied by the set QPS. #### Target | Fields | Description | Structures | Validation | Values | Defaults | |--------------------|-------------------------|----------------------------------------------|-----|------------|-------| -| targetUser | dns request to user-defined dns server | [targetUser](./netdns.md#targetuser) | Optional | | true | -| targetDns | Make a dns request to the cluster's dns server (coredns) | [targetDns](./netdns.md#targetuser) | Optional | | true | -| enableLatencyMetric | statisticsDemoDistribution,enable to increase memory usage | bool | optional | true,false | false | +| targetUser | DNS request to user-defined DNS server | [targetUser](./netdns.md#targetuser) | Optional | | True | +| targetDns | Make a DNS request to the cluster's DNS server (CoreDNS) | [targetDns](./netdns.md#targetuser) | Optional | |True | +| enableLatencyMetric | Statistics demo distribution, which increases memory usage when turned on |Bool | Optional | True,false | False | #### Expect -Task success condition, if the task result does not meet the expected condition, the task will fail. +Task success condition. If the task result does not meet the expected condition, the task will fail. -| fields | description | structure | validation | values | default | +| Fields | Description | Structures | Validation | Values | Default | | --------------------| ---------------------------------| -------| -----| --------| ------| -| meanAccessDelayInMs | meanAccessDelayInMs | The average delay, if the final result exceeds this value, the task will be judged as failed | int | optional | greater than or equal to 1 | 5000 | -| successRate | The success rate of the http request, if the final result is less than this value, the task will fail | float | optional | 0-1 | 1 | +|meanAccessDelayInMs | The average delay. If the final result exceeds this value, the task will be judged as failed | int | Optional | Greater than or equal to 1 | 5000 | +| successRate | Success rate of the HTTP request. If the final result is less than this value, the task will fail | Float | Optional | 0-1 | 1 | #### TargetUser -Test user customized dns server +Test user customized DNS server | Fields | Description | Structure | Validation | Values | Defaults | |-------------|---------------|--------|-----|---------|---| -| server | dns server address | string | mandatory | | | -| port | dns server port | int | required | 1-65535 | | +|Server | DNS Server Address | String | Required | | | +| Port | DNS Server Port | int | Required | 1-65535 | | #### TargetDns -Test dns server in cluster +Test the DNS server in a cluster -| fields | description | structure | validation | values | defaults | +| Fields | Description | Structure | Validation | Values | Defaults | |----------|--------------------------|--------|-----|-----------|-------| -| testIPv4 | testIPv4 | Test ipv4 address request A record | bool | optional | true,false | true | -| testIPv6 | Test ipv6 address request AAAA record | bool | optional | true,false | false | -| serviceName | cluster dns server service address | string | optional | | | -| serviceNamespace | cluster dns server service port | string | optional | | | +| testIPv4 | Test IPv4 address request A record | Bool | Optional | True,false | True | +| testIPv6 | Test IPv6 address request AAAA record | Bool | Optional | True,false | False | +| serviceName | Cluster DNS Server Service Address | String | Optional | | | +| serviceNamespace | Cluster DNS Server Service Port | String | Optional | | | -### status +### Status -| fields | description | structure | values | +| Fields | Description | Structures | Values | |--------------------|----------|------------------------------------------|-----------------| -| doneRound | number of completed task rounds | int | | -| expectedRound | number of rounds expected to be performed | int | | | -| finish | Whether the task is complete or not | bool | true, false | -| lastRoundStatus | lastRoundStatus | string | notstarted, on-going, succeed, fail | -| history | Task history | Element is [history](./apphttphealthy.md#history) array | | +| doneRound | Number of completed task rounds | int | | +| expectedRound | Number of rounds expected to be performed | int | | +| Finish | Whether the task is complete or not |Bool |True, false | +| lastRoundStatus | lastRoundStatus | String | Notstarted, on-going, succeed, fail | +| History | Task History | Element is [history](./apphttphealthy.md#history) array | | #### History | Fields | Description | Structure | Values | | ----------------------------------|-----------------|--------------|--------------------------------| -| roundNumber | Task round number | int | | -| status | Task Status | string | notstarted, on-going, succeed, fail | -| startTimeStamp | startTimeStamp | startTimeStamp | string | | -| startTimeStamp | startTimeStamp | startTimeStamp | endTimeStamp | endTimeStamp | endTimeStamp | string | | -| deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadline | string | | -| failedAgentNodeList | failedAgentNodeList | Array of failed agents | string | | -| notReportAgentNodeList | agent who did not upload a task report | array of elements as string | | notReportAgentNodeList | agent who failed to upload a task report | array of elements as string| +| roundNumber | Task Round Number | int | | +| Status | Task Status | String | Notstarted, on-going, succeed, fail | +| startTimeStamp | Start of the current round of tasks | String | | +| endTimeStamp | End of the current round of tasks | string | | +| duration |Execution time of the current round of tasks |string | | +| deadLineTimeStamp | Deadline of the current round of tasks | string | | +| failedAgentNodeList | Agent whose tasks failed |Array of elements as string | | +| succeedAgentNodeList |Agent whose task succeeded | Array of elements as string | | +| notReportAgentNodeList |Agent who did not upload a task report | Array of elements as string | | diff --git a/docs/reference/netreach-zh_CN.md b/docs/reference/netreach-zh_CN.md index ba381e72..8c6238c0 100644 --- a/docs/reference/netreach-zh_CN.md +++ b/docs/reference/netreach-zh_CN.md @@ -4,7 +4,7 @@ ## 基本描述 -对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent pod 都会相互发送http请求,请求地址为每一个 agent 的 pod ip 、service ip、ingress ip 等等,并获得成功率和平均延迟。它可以指定成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 +对于这种任务,kdoctor-controller 会根据 agentSpec 生成对应的 [agent](../concepts/runtime-zh_CN.md) 等资源,每一个 agent Pod 都会相互发送 http 请求,请求地址为每一个 agent 的 Pod IP、service IP、ingress IP 等等,并获得成功率和平均延迟。它可以指定成功条件来判断结果是否成功。并且,可以通过聚合API获取详细的报告。 ## NetReach 示例 @@ -72,7 +72,7 @@ status: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | 任务执行agent配置 | [agentSpec](./netreach-zh_CN.md#AgentSpec) | 可选 | | | +| agentSpec | 任务执行agent 配置 | [agentSpec](./netreach-zh_CN.md#AgentSpec) | 可选 | | | | schedule | 调度任务执行 | [schedule](./netreach-zh_CN.md#Schedule) | 可选 | | | | request | 对目标地址请求配置 | [request](./netreach-zh_CN.md#Request) | 可选 | | | | target | 请求目标设置 | [target](./netreach-zh_CN.md#Target) | 可选 | | | @@ -114,11 +114,11 @@ status: | 字段 | 描述 | 结构 | 验证 | 取值 | 默认值 | |--------------------|-------------------------|--------|-----|------------|-------| -| clusterIP | 测试集群 service cluster ip | bool | 可选 | true,false | true | -| endpoint | 测试集群 pod endpoint | bool | 可选 | true,false | true | -| multusInterface | 测试集群 pod multus 多网卡 ip | bool | 可选 | true,false | false | -| ipv4 | 测试 ipv4 | bool | 可选 | true,false | true | -| ipv6 | 测试 ipv6 | bool | 可选 | true,false | false | +| clusterIP | 测试集群 service cluster IP | bool | 可选 | true,false | true | +| endpoint | 测试集群 Pod endpoint | bool | 可选 | true,false | true | +| multusInterface | 测试集群 Pod multus 多网卡 IP | bool | 可选 | true,false | false | +| IPv4 | 测试 IPv4 | bool | 可选 | true,false | true | +| IPv6 | 测试 IPv6 | bool | 可选 | true,false | false | | ingress | 测试 ingress 地址 | bool | 可选 | true,false | false | | nodePort | 测试 service node port | bool | 可选 | true,false | true | | enableLatencyMetric | 统计演示分布,开启后会增加内存使用量 | bool | 可选 | true,false | false | diff --git a/docs/reference/netreach.md b/docs/reference/netreach.md index 84b9fa5b..63c42d71 100644 --- a/docs/reference/netreach.md +++ b/docs/reference/netreach.md @@ -4,7 +4,7 @@ ## Basic description -For this kind of task, kdoctor-controller will generate corresponding [agent](../concepts/runtime.md) and other resources, and each agent pod sends http requests to each other with the request address of each agent's pod ip, service ip, ingress ip and so on, and obtains the success rate and average latency. It can specify the success condition to determine whether the result is successful or not. And, detailed reports can be obtained through the aggregation API. +For this kind of task, kdoctor-controller will generate corresponding [agent](../concepts/runtime.md) and other resources. Each agent Pod sends http requests to each other with the request address of each agent's Pod IP, service IP, ingress IP and so on, and obtains the success rate and average latency. It can specify the success condition to determine whether the result is successful or not. Detailed reports can be obtained through the aggregation API. ## NetReach example @@ -64,92 +64,94 @@ status: ### Metadata -| fields | description | structure | validation | +| Fields | Description | Structure | Validation | |-----|---------------|--------|-----| -| name | Name of the NetReach resource | string | required | +| Name | Name of the NetReach Resource | String | Required | ### Spec -| fields | description | structure | validation | take values | default | +| Fields | Description | Structure | Validation | Values | Default | |-----------|-------------|--------------------------------------------|---------|-------|------| -| agentSpec | task execution agent configuration | [agentSpec](./apphttphealthy.md#agentspec) | optional | | | -| schedule |Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | optional | | | -| request |Request configuration for destination address | [request](./netdns.md#request) | Optional | | | -| target | Request target settings | [target](./apphttphealthy.md#target) | Optional | | | -| expect |Task success condition judgment | [expect](./apphttphealthy.md#expect) | Optional | | | +| agentSpec | Task Execution Agent Configuration | [agentSpec](./apphttphealthy.md#agentspec) | Optional | | | +| Schedule |Schedule Task Execution | [schedule](./apphttphealthy.md#schedule) | Optional | | | +|Request |Request Configuration for Destination Address | [request](./netdns.md#request) | Optional | | | +|Target | Request Target Settings | [target](./apphttphealthy.md#target) | Optional | | | +|Expect |Task Success Condition Judgment | [expect](./apphttphealthy.md#expect) | Optional | | | #### AgentSpec | Fields | Description | Structure | Validation | Values | Default | |-------------------------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----|----------------------|-------------------------------| -| annotation | annotation of agent workload | map[string]string | optional | | | | -| kind | type of agent workload | string | optional | Deployment, DaemonSet | DaemonSet | -| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | optional | greater than or equal to 0 | 0 | -| affinity | agent workload affinity | labelSelector | optional | | | -| env | agent workload environment variable | env | optional | | | | hostNetwork | agent -| hostNetwork | agent Whether or not the workload uses the host network | bool | optional | true, false | false | -| resources | agent workload resource usage configuration | resources | optional | | limit cpu:1000m,memory:1024Mi | -| terminationGracePeriodMinutes | agent How many minutes after a workload completes a task before it terminates | int | optional | greater than or equal to 0 | 60 | +|Annotation | Annotation of Agent Workload |Map[string]String | Optional | | | +| kind |Type of Agent Workload | String | Optional | Deployment, DaemonSet | DaemonSet | +| deploymentReplicas | The expected number of replicas when the agent workload type is deployment | int | Optional | Greater than or equal to 0 | 0 | +|Affinity | Agent Workload Affinity | labelSelector | Optional | | | +| env | Agent Workload Environment Variable | env |Optional | | | +| hostNetwork | Whether or not the agent workload uses the host network | Bool | Optional | True, false | False | +| Resources | Agent Workload Resource Usage Configuration | Resources | Optional | | Limit cpu: 1000m,Memory:1024Mi | +| terminationGracePeriodMinutes | the minutes after a agent workload completes a task before it terminates | int | Optional |Greater than or equal to 0 | 60 | #### Schedule | Fields | Description | Structure | Validation | Values | Defaults | |--------------------|---------------------------------------|--------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| -| roundNumber | task execution rounds | int | optional | greater than or equal to -1, -1 means permanent, greater than 0 means rounds will be executed | 1 | schedule | task execution time, execution time should be less than roundTimeoutMinute | string | optional | Support linux crontab and interval method.
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: written in "M N" format, M takes the value of a number, indicating how many minutes after opening the task, N takes the value of a number, indicating how many minutes between each round of task execution, for example, "0 1" means start the task immediately, each round of task interval 1min | "0 1" | roundTimeoutMininute -| roundTimeoutMinute | Task timeout, needs to be greater than durationInSecond and task execution time | int | optional | greater than or equal to 1 | 60 | +| roundNumber |Task Execution Rounds | int | Optional | A value greater than or equal to -1 indicates indefinite execution, with -1 representing permanent execution. A value greater than 0 represents the number of rounds to be executed | 1 | Schedule | Task execution time which should be less than roundTimeoutMinute | String | Optional | Support linux crontab and interval method
    [linux crontab](https://linuxhandbook.com/crontab/) : */1 * * * * * means execute every minute
    Interval method: writing format "M N". M is a number that indicates how many minutes after the task is started; N is a number that indicates how many minutes between each round of tasks. For example, "0 1" means start the task immediately, 1min between each round of tasks. | "0 60" | +| roundTimeoutMinute | Task timeout which needs to be greater than durationInSecond and task execution time | int | Optional | Greater than or equal to 1 | 60 | #### Request | Fields | Description | Structure | Validation | Values | Defaults | |------------------------|---------------------------------------|--------|-----|---------------|---------------| -| durationInSecond | Duration of request send pressure for each round of tasks less than roundTimeoutMinute | int | optional | greater than or equal to 1 | 2 | -| perRequestTimeoutInMS | timeout per request, not greater than durationInSecond | int | optional | greater than or equal to 1 | 500 | -| qps | requests per second per agent | int | optional | greater than or equal to 1 | 5 | +| durationInSecond | Duration of request send pressure for each round of tasks which is less than roundTimeoutMinute | int |Optional | Greater than or equal to 1 | 2 | +| perRequestTimeoutInMS | Timeout per request, not greater than durationInSecond | int |Optional | Greater than or equal to 1 | 500 | +| QPS | Requests per second per agent | int | Optional | Greater than or equal to 1 | 5 | -> Note: When using agent requests, all agents make requests to the destination address, so the actual qps received by the server is equal to the number of agents * the set qps. +> When using agent requests, all agents will make requests to the destination address, so the actual QPS received by the server is equal to the number of agents multiplied by the set QPS. #### Target | Fields | Descriptions | Structures | Validations | Values | Defaults | |--------------------|-------------------------|--------|-----|------------|-------| -| clusterIP | Test cluster service's cluster ip | bool | Optional | true,false | true | -| endpoint | Test cluster pod endpoint | bool | Optional | true,false | true | -| multusInterface | Test cluster pod multus multi-NIC ip | bool | Optional | true,false | false | -| ipv4 | Test ipv4 | bool | Optional | true,false | true | -| ipv6 | Test ipv6 | bool | Optional | true,false | false | -| ingress | Test ingress address | bool | Optional | true,false | false | -| nodePort | test service node port | bool | Optional | true,false | true | -| enableLatencyMetric | Count demo distribution, when turned on it will increase memory usage | bool | Optional | true,false | false | +| ClusterIP | Test cluster service's cluster IP | Bool | Optional | True,false |True | +| Endpoint | Test cluster Pod endpoint | Bool | Optional | True,false | True | +| multusInterface | Test cluster Pod Multus multi-NIC IP | Bool | Optional | True,false | False | +| IPv4 | Test IPv4 | Bool | Optional | True,false | True | +| IPv6 | Test IPv6 | Bool | Optional | True,false |False | +|Ingress | Test Ingress Address | Bool | Optional | True,false | False | +| nodePort | Test Service Node Port | Bool | Optional | True,false | True | +| enableLatencyMetric | Statistics demo distribution, which increases memory usage when turned on | Bool | Optional | True,false |False | #### Expect -Task success condition, if the task result does not meet the expected condition, the task will fail. +Task success condition. If the task result does not meet the expected condition, the task will fail. -| fields | description | structure | validation | values | default | +| Fields | Description | Structures | Validation | Values | Default | | --------------------| ---------------------------------| -------| -----| --------| ------| -| meanAccessDelayInMs | meanAccessDelayInMs | The average delay, if the final result exceeds this value, the task will be judged as failed | int | optional | greater than or equal to 1 | 5000 | -| successRate | The success rate of the http request, if the final result is less than this value, the task will fail | float | optional | 0-1 | 1 | +|meanAccessDelayInMs | The average delay. If the final result exceeds this value, the task will be judged as failed | int | Optional | Greater than or equal to 1 | 5000 | +| successRate | Success rate of the HTTP request. If the final result is less than this value, the task will fail | Float | Optional | 0-1 | 1 | ### status -| fields | description | structure | values | +| Fields | Description | Structures | Values | |--------------------|----------|------------------------------------------|-----------------| -| doneRound | number of completed task rounds | int | | -| expectedRound | number of rounds expected to be performed | int | | | -| finish | Whether the task is complete or not | bool | true, false | -| lastRoundStatus | lastRoundStatus | string | notstarted, on-going, succeed, fail | -| history | Task history | Element is [history](./apphttphealthy.md#history) array | | +| doneRound | Number of completed task rounds | int | | +| expectedRound | Number of rounds expected to be performed | int | | +| Finish | Whether the task is complete or not |Bool |True, false | +| lastRoundStatus | lastRoundStatus | String | Notstarted, on-going, succeed, fail | +| History | Task History | Element is [history](./apphttphealthy.md#history) array | | #### History | Fields | Description | Structure | Values | | ----------------------------------|-----------------|--------------|--------------------------------| -| roundNumber | Task round number | int | | -| status | Task Status | string | notstarted, on-going, succeed, fail | -| startTimeStamp | startTimeStamp | startTimeStamp | string | | -| startTimeStamp | startTimeStamp | startTimeStamp | endTimeStamp | endTimeStamp | endTimeStamp | string | | -| deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadLineTimeStamp | deadline | string | | -| failedAgentNodeList | failedAgentNodeList | Array of failed agents | string | | -| notReportAgentNodeList | agent who did not upload a task report | array of elements as string | | notReportAgentNodeList | agent who failed to upload a task report | array of elements as string| +| roundNumber | Task Round Number | int | | +| Status | Task Status | String | Notstarted, on-going, succeed, fail | +| startTimeStamp | Start of the current round of tasks | String | | +| endTimeStamp | End of the current round of tasks | string | | +| duration |Execution time of the current round of tasks |string | | +| deadLineTimeStamp | Deadline of the current round of tasks | string | | +| failedAgentNodeList | Agent whose tasks failed |Array of elements as string | | +| succeedAgentNodeList |Agent whose task succeeded | Array of elements as string | | +| notReportAgentNodeList |Agent who did not upload a task report | Array of elements as string | | diff --git a/docs/reference/report.md b/docs/reference/report.md index 7e3947ff..17355c21 100644 --- a/docs/reference/report.md +++ b/docs/reference/report.md @@ -1,16 +1,13 @@ -# +## Agent Report +When agent finish task, it saves report to `/report` with name fmt.Sprintf("%s_%s_round%d_%s_%s", kindName, taskName, roundNumber, nodeName, suffix). +The report will be automatically deleted with age `spec.schedulePlan.TimeoutMinute + 5` minutes. In this interval, +the controller Pod will collect this report and save to `/report` of controller Pod. -## agent report +## Controller Report -When agent finish task, it saves report to '/report' with name fmt.Sprintf("%s_%s_round%d_%s_%s", kindName, taskName, roundNumber, nodeName, suffix). -the report will be automatically deleted with age 'spec.schedulePlan.TimeoutMinute + 5 ' minutes. In this interval , -the controller pod will collect this report and save to '/report' of controller pod +When task finishes, it saves report to `/report` with name fmt.Sprintf("%s_%s_round%d_%s_%s", kindName, taskName, roundNumber, nodeName, suffix). +It also collects all agent report and saves report to `/report`. +All files in `/report` of controller will survive with max age maxAgeInDay(default 30 days). It could be adjusted in the configmap. -## controller report - -when task finishes, it saves report to '/report' with name fmt.Sprintf("%s_%s_round%d_%s_%s", kindName, taskName, roundNumber, nodeName, suffix). -It also collects all agent report and saves report to '/report'. -All files in '/report' of controller will sevive with max age maxAgeInDay(default 30 days). It could be adjusted in the configmap - -the controller could save reports to host path or PVC +The controller could save reports to host path or PVC. From 2750e7579fefa35922499030bb4afb068a9588ef Mon Sep 17 00:00:00 2001 From: ii2day Date: Thu, 14 Sep 2023 14:55:55 +0800 Subject: [PATCH 13/41] add unitest Signed-off-by: ii2day --- go.mod | 18 +- go.sum | 42 +- pkg/fileManager/fileManager_suite_test.go | 4 +- pkg/fileManager/manager.go | 41 - pkg/fileManager/manager_test.go | 13 +- pkg/fileManager/tools_test.go | 3 +- pkg/grpcManager/mock/client_mock.go | 83 ++ pkg/k8ObjManager/mock/manager_mock.go | 385 ++++++++ pkg/loadRequest/loadDns/dns_suite_test.go | 2 +- pkg/loadRequest/loadHttp/http_suite_test.go | 22 + pkg/loadRequest/loadHttp/http_test.go | 69 ++ pkg/logger/logger_suite_test.go | 19 + pkg/logger/logger_test.go | 33 + pkg/pluginManager/controllerManager.go | 4 +- pkg/pluginManager/controllerTools.go | 1 - pkg/reportManager/manager.go | 40 +- pkg/reportManager/manager_test.go | 85 +- pkg/reportManager/reportManager_suite_test.go | 16 +- pkg/reportManager/worker.go | 32 +- pkg/reportManager/worker_test.go | 80 ++ pkg/resource/resource_suite_test.go | 20 + pkg/resource/resource_test.go | 26 + pkg/runningTask/runningTask_suite_test.go | 20 + pkg/runningTask/runningTask_test.go | 35 + pkg/scheduler/cachedb.go | 11 +- pkg/scheduler/cachedb_test.go | 89 ++ pkg/scheduler/runtime/runtime_suite_test.go | 34 + pkg/scheduler/runtime/runtime_test.go | 74 ++ pkg/scheduler/schedule_test.go | 93 ++ pkg/scheduler/scheduler_suite_test.go | 80 ++ pkg/scheduler/tracing_test.go | 229 +++++ pkg/taskStatusManager/date_suite_test.go | 19 + pkg/taskStatusManager/date_test.go | 18 + pkg/utils/utils.go | 21 - test/e2e/netdns/netdns_test.go | 1 + .../agiledragon/gomonkey/v2/LICENSE | 21 + .../agiledragon/gomonkey/v2/README.md | 54 + .../gomonkey/v2/creflect/ae1.17.go | 39 + .../gomonkey/v2/creflect/be1.16.go | 25 + .../agiledragon/gomonkey/v2/creflect/type.go | 179 ++++ .../agiledragon/gomonkey/v2/jmp_386.go | 13 + .../agiledragon/gomonkey/v2/jmp_amd64.go | 18 + .../agiledragon/gomonkey/v2/jmp_arm64.go | 37 + .../agiledragon/gomonkey/v2/jmp_loong64.go | 73 ++ .../gomonkey/v2/modify_binary_darwin.go | 24 + .../gomonkey/v2/modify_binary_linux.go | 27 + .../gomonkey/v2/modify_binary_windows.go | 25 + .../agiledragon/gomonkey/v2/patch.go | 365 +++++++ .../gomonkey/v2/write_darwin_amd64.s | 64 ++ .../gomonkey/v2/write_darwin_arm64.s | 63 ++ vendor/github.com/golang/mock/AUTHORS | 12 + vendor/github.com/golang/mock/CONTRIBUTORS | 37 + vendor/github.com/golang/mock/LICENSE | 202 ++++ vendor/github.com/golang/mock/gomock/call.go | 445 +++++++++ .../github.com/golang/mock/gomock/callset.go | 113 +++ .../golang/mock/gomock/controller.go | 336 +++++++ .../github.com/golang/mock/gomock/matchers.go | 341 +++++++ .../x/crypto/internal/alias/alias.go | 1 - .../x/crypto/internal/alias/alias_purego.go | 1 - .../x/crypto/internal/poly1305/bits_compat.go | 1 - .../x/crypto/internal/poly1305/bits_go1.13.go | 1 - .../x/crypto/internal/poly1305/mac_noasm.go | 1 - .../x/crypto/internal/poly1305/sum_amd64.go | 1 - .../x/crypto/internal/poly1305/sum_amd64.s | 1 - .../x/crypto/internal/poly1305/sum_ppc64le.go | 1 - .../x/crypto/internal/poly1305/sum_ppc64le.s | 1 - .../x/crypto/internal/poly1305/sum_s390x.go | 1 - .../x/crypto/internal/poly1305/sum_s390x.s | 1 - .../x/crypto/salsa20/salsa/salsa20_amd64.go | 1 - .../x/crypto/salsa20/salsa/salsa20_amd64.s | 1 - .../x/crypto/salsa20/salsa/salsa20_noasm.go | 1 - .../x/mod/internal/lazyregexp/lazyre.go | 2 +- vendor/golang.org/x/mod/module/module.go | 30 +- vendor/golang.org/x/mod/module/pseudo.go | 2 +- vendor/golang.org/x/mod/semver/semver.go | 6 +- vendor/golang.org/x/net/context/go17.go | 1 - vendor/golang.org/x/net/context/go19.go | 1 - vendor/golang.org/x/net/context/pre_go17.go | 1 - vendor/golang.org/x/net/context/pre_go19.go | 1 - vendor/golang.org/x/net/http2/databuffer.go | 59 +- vendor/golang.org/x/net/http2/go111.go | 30 - vendor/golang.org/x/net/http2/go115.go | 27 - vendor/golang.org/x/net/http2/go118.go | 17 - vendor/golang.org/x/net/http2/not_go111.go | 21 - vendor/golang.org/x/net/http2/not_go115.go | 31 - vendor/golang.org/x/net/http2/not_go118.go | 17 - vendor/golang.org/x/net/http2/server.go | 24 +- vendor/golang.org/x/net/http2/transport.go | 33 +- vendor/golang.org/x/net/idna/go118.go | 1 - vendor/golang.org/x/net/idna/idna10.0.0.go | 1 - vendor/golang.org/x/net/idna/idna9.0.0.go | 1 - vendor/golang.org/x/net/idna/pre_go118.go | 1 - vendor/golang.org/x/net/idna/tables10.0.0.go | 1 - vendor/golang.org/x/net/idna/tables11.0.0.go | 1 - vendor/golang.org/x/net/idna/tables12.0.0.go | 1 - vendor/golang.org/x/net/idna/tables13.0.0.go | 1 - vendor/golang.org/x/net/idna/tables15.0.0.go | 1 - vendor/golang.org/x/net/idna/tables9.0.0.go | 1 - vendor/golang.org/x/net/idna/trie12.0.0.go | 1 - vendor/golang.org/x/net/idna/trie13.0.0.go | 1 - .../x/net/internal/socket/cmsghdr.go | 1 - .../x/net/internal/socket/cmsghdr_bsd.go | 1 - .../internal/socket/cmsghdr_linux_32bit.go | 2 - .../internal/socket/cmsghdr_linux_64bit.go | 2 - .../internal/socket/cmsghdr_solaris_64bit.go | 1 - .../x/net/internal/socket/cmsghdr_stub.go | 1 - .../x/net/internal/socket/cmsghdr_unix.go | 1 - .../net/internal/socket/complete_dontwait.go | 1 - .../internal/socket/complete_nodontwait.go | 1 - .../golang.org/x/net/internal/socket/empty.s | 1 - .../x/net/internal/socket/error_unix.go | 1 - .../x/net/internal/socket/iovec_32bit.go | 2 - .../x/net/internal/socket/iovec_64bit.go | 2 - .../internal/socket/iovec_solaris_64bit.go | 1 - .../x/net/internal/socket/iovec_stub.go | 1 - .../x/net/internal/socket/mmsghdr_stub.go | 1 - .../x/net/internal/socket/mmsghdr_unix.go | 1 - .../x/net/internal/socket/msghdr_bsd.go | 1 - .../x/net/internal/socket/msghdr_bsdvar.go | 1 - .../net/internal/socket/msghdr_linux_32bit.go | 2 - .../net/internal/socket/msghdr_linux_64bit.go | 2 - .../internal/socket/msghdr_solaris_64bit.go | 1 - .../x/net/internal/socket/msghdr_stub.go | 1 - .../x/net/internal/socket/msghdr_zos_s390x.go | 1 - .../x/net/internal/socket/norace.go | 1 - .../golang.org/x/net/internal/socket/race.go | 1 - .../x/net/internal/socket/rawconn_mmsg.go | 1 - .../x/net/internal/socket/rawconn_msg.go | 1 - .../x/net/internal/socket/rawconn_nommsg.go | 1 - .../x/net/internal/socket/rawconn_nomsg.go | 1 - .../x/net/internal/socket/sys_bsd.go | 1 - .../x/net/internal/socket/sys_const_unix.go | 1 - .../x/net/internal/socket/sys_linux.go | 1 - .../net/internal/socket/sys_linux_loong64.go | 1 - .../net/internal/socket/sys_linux_riscv64.go | 1 - .../x/net/internal/socket/sys_posix.go | 1 - .../x/net/internal/socket/sys_stub.go | 1 - .../x/net/internal/socket/sys_unix.go | 1 - .../x/net/internal/socket/zsys_aix_ppc64.go | 1 - .../net/internal/socket/zsys_linux_loong64.go | 1 - .../net/internal/socket/zsys_linux_riscv64.go | 1 - vendor/golang.org/x/net/ipv4/control_bsd.go | 1 - .../golang.org/x/net/ipv4/control_pktinfo.go | 1 - vendor/golang.org/x/net/ipv4/control_stub.go | 1 - vendor/golang.org/x/net/ipv4/control_unix.go | 1 - vendor/golang.org/x/net/ipv4/icmp_stub.go | 1 - vendor/golang.org/x/net/ipv4/payload_cmsg.go | 1 - .../golang.org/x/net/ipv4/payload_nocmsg.go | 1 - vendor/golang.org/x/net/ipv4/sockopt_posix.go | 1 - vendor/golang.org/x/net/ipv4/sockopt_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_aix.go | 1 - vendor/golang.org/x/net/ipv4/sys_asmreq.go | 1 - .../golang.org/x/net/ipv4/sys_asmreq_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_asmreqn.go | 1 - .../golang.org/x/net/ipv4/sys_asmreqn_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_bpf.go | 1 - vendor/golang.org/x/net/ipv4/sys_bpf_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_bsd.go | 1 - vendor/golang.org/x/net/ipv4/sys_ssmreq.go | 1 - .../golang.org/x/net/ipv4/sys_ssmreq_stub.go | 1 - vendor/golang.org/x/net/ipv4/sys_stub.go | 1 - .../golang.org/x/net/ipv4/zsys_aix_ppc64.go | 1 - .../x/net/ipv4/zsys_linux_loong64.go | 1 - .../x/net/ipv4/zsys_linux_riscv64.go | 1 - .../x/net/ipv6/control_rfc2292_unix.go | 1 - .../x/net/ipv6/control_rfc3542_unix.go | 1 - vendor/golang.org/x/net/ipv6/control_stub.go | 1 - vendor/golang.org/x/net/ipv6/control_unix.go | 1 - vendor/golang.org/x/net/ipv6/icmp_bsd.go | 1 - vendor/golang.org/x/net/ipv6/icmp_stub.go | 1 - vendor/golang.org/x/net/ipv6/payload_cmsg.go | 1 - .../golang.org/x/net/ipv6/payload_nocmsg.go | 1 - vendor/golang.org/x/net/ipv6/sockopt_posix.go | 1 - vendor/golang.org/x/net/ipv6/sockopt_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_aix.go | 1 - vendor/golang.org/x/net/ipv6/sys_asmreq.go | 1 - .../golang.org/x/net/ipv6/sys_asmreq_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_bpf.go | 1 - vendor/golang.org/x/net/ipv6/sys_bpf_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_bsd.go | 1 - vendor/golang.org/x/net/ipv6/sys_ssmreq.go | 1 - .../golang.org/x/net/ipv6/sys_ssmreq_stub.go | 1 - vendor/golang.org/x/net/ipv6/sys_stub.go | 1 - .../golang.org/x/net/ipv6/zsys_aix_ppc64.go | 1 - .../x/net/ipv6/zsys_linux_loong64.go | 1 - .../x/net/ipv6/zsys_linux_riscv64.go | 1 - .../x/sync/singleflight/singleflight.go | 9 + vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s | 1 - vendor/golang.org/x/sys/cpu/cpu_aix.go | 1 - vendor/golang.org/x/sys/cpu/cpu_arm64.s | 1 - vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go | 1 - vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go | 1 - vendor/golang.org/x/sys/cpu/cpu_gc_x86.go | 2 - .../golang.org/x/sys/cpu/cpu_gccgo_arm64.go | 1 - .../golang.org/x/sys/cpu/cpu_gccgo_s390x.go | 1 - vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c | 2 - vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go | 2 - vendor/golang.org/x/sys/cpu/cpu_linux.go | 1 - .../golang.org/x/sys/cpu/cpu_linux_mips64x.go | 2 - .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 1 - .../golang.org/x/sys/cpu/cpu_linux_ppc64x.go | 2 - vendor/golang.org/x/sys/cpu/cpu_loong64.go | 1 - vendor/golang.org/x/sys/cpu/cpu_mips64x.go | 1 - vendor/golang.org/x/sys/cpu/cpu_mipsx.go | 1 - vendor/golang.org/x/sys/cpu/cpu_other_arm.go | 1 - .../golang.org/x/sys/cpu/cpu_other_arm64.go | 1 - .../golang.org/x/sys/cpu/cpu_other_mips64x.go | 2 - .../golang.org/x/sys/cpu/cpu_other_ppc64x.go | 3 - .../golang.org/x/sys/cpu/cpu_other_riscv64.go | 1 - vendor/golang.org/x/sys/cpu/cpu_ppc64x.go | 1 - vendor/golang.org/x/sys/cpu/cpu_riscv64.go | 1 - vendor/golang.org/x/sys/cpu/cpu_s390x.s | 1 - vendor/golang.org/x/sys/cpu/cpu_wasm.go | 1 - vendor/golang.org/x/sys/cpu/cpu_x86.go | 1 - vendor/golang.org/x/sys/cpu/cpu_x86.s | 2 - vendor/golang.org/x/sys/cpu/endian_big.go | 1 - vendor/golang.org/x/sys/cpu/endian_little.go | 1 - .../x/sys/cpu/proc_cpuinfo_linux.go | 1 - .../x/sys/cpu/runtime_auxv_go121.go | 1 - .../golang.org/x/sys/cpu/syscall_aix_gccgo.go | 1 - .../x/sys/cpu/syscall_aix_ppc64_gc.go | 1 - .../golang.org/x/sys/execabs/execabs_go118.go | 1 - .../golang.org/x/sys/execabs/execabs_go119.go | 1 - .../golang.org/x/sys/plan9/pwd_go15_plan9.go | 1 - vendor/golang.org/x/sys/plan9/pwd_plan9.go | 1 - vendor/golang.org/x/sys/plan9/race.go | 1 - vendor/golang.org/x/sys/plan9/race0.go | 1 - vendor/golang.org/x/sys/plan9/str.go | 1 - vendor/golang.org/x/sys/plan9/syscall.go | 1 - .../x/sys/plan9/zsyscall_plan9_386.go | 1 - .../x/sys/plan9/zsyscall_plan9_amd64.go | 1 - .../x/sys/plan9/zsyscall_plan9_arm.go | 1 - vendor/golang.org/x/sys/unix/aliases.go | 2 - vendor/golang.org/x/sys/unix/asm_aix_ppc64.s | 1 - vendor/golang.org/x/sys/unix/asm_bsd_386.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_amd64.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_arm.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_arm64.s | 2 - vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s | 2 - .../golang.org/x/sys/unix/asm_bsd_riscv64.s | 2 - vendor/golang.org/x/sys/unix/asm_linux_386.s | 1 - .../golang.org/x/sys/unix/asm_linux_amd64.s | 1 - vendor/golang.org/x/sys/unix/asm_linux_arm.s | 1 - .../golang.org/x/sys/unix/asm_linux_arm64.s | 3 - .../golang.org/x/sys/unix/asm_linux_loong64.s | 3 - .../golang.org/x/sys/unix/asm_linux_mips64x.s | 3 - .../golang.org/x/sys/unix/asm_linux_mipsx.s | 3 - .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 3 - .../golang.org/x/sys/unix/asm_linux_riscv64.s | 2 - .../golang.org/x/sys/unix/asm_linux_s390x.s | 3 - .../x/sys/unix/asm_openbsd_mips64.s | 1 - .../golang.org/x/sys/unix/asm_solaris_amd64.s | 1 - vendor/golang.org/x/sys/unix/asm_zos_s390x.s | 3 - vendor/golang.org/x/sys/unix/cap_freebsd.go | 1 - vendor/golang.org/x/sys/unix/constants.go | 1 - vendor/golang.org/x/sys/unix/dev_aix_ppc.go | 1 - vendor/golang.org/x/sys/unix/dev_aix_ppc64.go | 1 - vendor/golang.org/x/sys/unix/dev_zos.go | 1 - vendor/golang.org/x/sys/unix/dirent.go | 1 - vendor/golang.org/x/sys/unix/endian_big.go | 1 - vendor/golang.org/x/sys/unix/endian_little.go | 1 - vendor/golang.org/x/sys/unix/env_unix.go | 1 - vendor/golang.org/x/sys/unix/epoll_zos.go | 1 - vendor/golang.org/x/sys/unix/fcntl.go | 1 - .../x/sys/unix/fcntl_linux_32bit.go | 1 - vendor/golang.org/x/sys/unix/fdset.go | 1 - vendor/golang.org/x/sys/unix/fstatfs_zos.go | 1 - vendor/golang.org/x/sys/unix/gccgo.go | 1 - vendor/golang.org/x/sys/unix/gccgo_c.c | 1 - .../x/sys/unix/gccgo_linux_amd64.go | 1 - vendor/golang.org/x/sys/unix/ifreq_linux.go | 1 - vendor/golang.org/x/sys/unix/ioctl_signed.go | 1 - .../golang.org/x/sys/unix/ioctl_unsigned.go | 1 - vendor/golang.org/x/sys/unix/ioctl_zos.go | 1 - vendor/golang.org/x/sys/unix/mkerrors.sh | 1 - vendor/golang.org/x/sys/unix/mmap_nomremap.go | 1 - vendor/golang.org/x/sys/unix/mremap.go | 1 - vendor/golang.org/x/sys/unix/pagesize_unix.go | 1 - .../golang.org/x/sys/unix/pledge_openbsd.go | 92 +- vendor/golang.org/x/sys/unix/ptrace_darwin.go | 1 - vendor/golang.org/x/sys/unix/ptrace_ios.go | 1 - vendor/golang.org/x/sys/unix/race.go | 1 - vendor/golang.org/x/sys/unix/race0.go | 1 - .../x/sys/unix/readdirent_getdents.go | 1 - .../x/sys/unix/readdirent_getdirentries.go | 1 - vendor/golang.org/x/sys/unix/sockcmsg_unix.go | 1 - .../x/sys/unix/sockcmsg_unix_other.go | 1 - vendor/golang.org/x/sys/unix/syscall.go | 1 - vendor/golang.org/x/sys/unix/syscall_aix.go | 4 +- .../golang.org/x/sys/unix/syscall_aix_ppc.go | 1 - .../x/sys/unix/syscall_aix_ppc64.go | 1 - vendor/golang.org/x/sys/unix/syscall_bsd.go | 1 - .../x/sys/unix/syscall_darwin_amd64.go | 1 - .../x/sys/unix/syscall_darwin_arm64.go | 1 - .../x/sys/unix/syscall_darwin_libSystem.go | 1 - .../x/sys/unix/syscall_dragonfly_amd64.go | 1 - .../x/sys/unix/syscall_freebsd_386.go | 1 - .../x/sys/unix/syscall_freebsd_amd64.go | 1 - .../x/sys/unix/syscall_freebsd_arm.go | 1 - .../x/sys/unix/syscall_freebsd_arm64.go | 1 - .../x/sys/unix/syscall_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/syscall_hurd.go | 1 - .../golang.org/x/sys/unix/syscall_hurd_386.go | 1 - .../golang.org/x/sys/unix/syscall_illumos.go | 1 - vendor/golang.org/x/sys/unix/syscall_linux.go | 5 +- .../x/sys/unix/syscall_linux_386.go | 1 - .../x/sys/unix/syscall_linux_alarm.go | 2 - .../x/sys/unix/syscall_linux_amd64.go | 1 - .../x/sys/unix/syscall_linux_amd64_gc.go | 1 - .../x/sys/unix/syscall_linux_arm.go | 1 - .../x/sys/unix/syscall_linux_arm64.go | 1 - .../golang.org/x/sys/unix/syscall_linux_gc.go | 1 - .../x/sys/unix/syscall_linux_gc_386.go | 1 - .../x/sys/unix/syscall_linux_gc_arm.go | 1 - .../x/sys/unix/syscall_linux_gccgo_386.go | 1 - .../x/sys/unix/syscall_linux_gccgo_arm.go | 1 - .../x/sys/unix/syscall_linux_loong64.go | 1 - .../x/sys/unix/syscall_linux_mips64x.go | 2 - .../x/sys/unix/syscall_linux_mipsx.go | 2 - .../x/sys/unix/syscall_linux_ppc.go | 1 - .../x/sys/unix/syscall_linux_ppc64x.go | 2 - .../x/sys/unix/syscall_linux_riscv64.go | 1 - .../x/sys/unix/syscall_linux_s390x.go | 1 - .../x/sys/unix/syscall_linux_sparc64.go | 1 - .../x/sys/unix/syscall_netbsd_386.go | 1 - .../x/sys/unix/syscall_netbsd_amd64.go | 1 - .../x/sys/unix/syscall_netbsd_arm.go | 1 - .../x/sys/unix/syscall_netbsd_arm64.go | 1 - .../golang.org/x/sys/unix/syscall_openbsd.go | 14 +- .../x/sys/unix/syscall_openbsd_386.go | 1 - .../x/sys/unix/syscall_openbsd_amd64.go | 1 - .../x/sys/unix/syscall_openbsd_arm.go | 1 - .../x/sys/unix/syscall_openbsd_arm64.go | 1 - .../x/sys/unix/syscall_openbsd_libc.go | 1 - .../x/sys/unix/syscall_openbsd_ppc64.go | 1 - .../x/sys/unix/syscall_openbsd_riscv64.go | 1 - .../golang.org/x/sys/unix/syscall_solaris.go | 3 +- .../x/sys/unix/syscall_solaris_amd64.go | 1 - vendor/golang.org/x/sys/unix/syscall_unix.go | 1 - .../golang.org/x/sys/unix/syscall_unix_gc.go | 2 - .../x/sys/unix/syscall_unix_gc_ppc64x.go | 3 - .../x/sys/unix/syscall_zos_s390x.go | 1 - vendor/golang.org/x/sys/unix/sysvshm_linux.go | 1 - vendor/golang.org/x/sys/unix/sysvshm_unix.go | 1 - .../x/sys/unix/sysvshm_unix_other.go | 1 - vendor/golang.org/x/sys/unix/timestruct.go | 1 - .../golang.org/x/sys/unix/unveil_openbsd.go | 41 +- vendor/golang.org/x/sys/unix/xattr_bsd.go | 1 - .../golang.org/x/sys/unix/zerrors_aix_ppc.go | 1 - .../x/sys/unix/zerrors_aix_ppc64.go | 1 - .../x/sys/unix/zerrors_darwin_amd64.go | 1 - .../x/sys/unix/zerrors_darwin_arm64.go | 1 - .../x/sys/unix/zerrors_dragonfly_amd64.go | 1 - .../x/sys/unix/zerrors_freebsd_386.go | 1 - .../x/sys/unix/zerrors_freebsd_amd64.go | 1 - .../x/sys/unix/zerrors_freebsd_arm.go | 1 - .../x/sys/unix/zerrors_freebsd_arm64.go | 1 - .../x/sys/unix/zerrors_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/zerrors_linux.go | 14 +- .../x/sys/unix/zerrors_linux_386.go | 1 - .../x/sys/unix/zerrors_linux_amd64.go | 1 - .../x/sys/unix/zerrors_linux_arm.go | 1 - .../x/sys/unix/zerrors_linux_arm64.go | 1 - .../x/sys/unix/zerrors_linux_loong64.go | 2 +- .../x/sys/unix/zerrors_linux_mips.go | 1 - .../x/sys/unix/zerrors_linux_mips64.go | 1 - .../x/sys/unix/zerrors_linux_mips64le.go | 1 - .../x/sys/unix/zerrors_linux_mipsle.go | 1 - .../x/sys/unix/zerrors_linux_ppc.go | 1 - .../x/sys/unix/zerrors_linux_ppc64.go | 1 - .../x/sys/unix/zerrors_linux_ppc64le.go | 1 - .../x/sys/unix/zerrors_linux_riscv64.go | 4 +- .../x/sys/unix/zerrors_linux_s390x.go | 1 - .../x/sys/unix/zerrors_linux_sparc64.go | 1 - .../x/sys/unix/zerrors_netbsd_386.go | 1 - .../x/sys/unix/zerrors_netbsd_amd64.go | 1 - .../x/sys/unix/zerrors_netbsd_arm.go | 1 - .../x/sys/unix/zerrors_netbsd_arm64.go | 1 - .../x/sys/unix/zerrors_openbsd_386.go | 1 - .../x/sys/unix/zerrors_openbsd_amd64.go | 1 - .../x/sys/unix/zerrors_openbsd_arm.go | 1 - .../x/sys/unix/zerrors_openbsd_arm64.go | 1 - .../x/sys/unix/zerrors_openbsd_mips64.go | 1 - .../x/sys/unix/zerrors_openbsd_ppc64.go | 1 - .../x/sys/unix/zerrors_openbsd_riscv64.go | 1 - .../x/sys/unix/zerrors_solaris_amd64.go | 1 - .../x/sys/unix/zerrors_zos_s390x.go | 1 - .../x/sys/unix/zptrace_armnn_linux.go | 2 - .../x/sys/unix/zptrace_mipsnn_linux.go | 2 - .../x/sys/unix/zptrace_mipsnnle_linux.go | 2 - .../x/sys/unix/zptrace_x86_linux.go | 2 - .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64_gc.go | 1 - .../x/sys/unix/zsyscall_aix_ppc64_gccgo.go | 1 - .../x/sys/unix/zsyscall_darwin_amd64.go | 1 - .../x/sys/unix/zsyscall_darwin_arm64.go | 1 - .../x/sys/unix/zsyscall_dragonfly_amd64.go | 1 - .../x/sys/unix/zsyscall_freebsd_386.go | 1 - .../x/sys/unix/zsyscall_freebsd_amd64.go | 1 - .../x/sys/unix/zsyscall_freebsd_arm.go | 1 - .../x/sys/unix/zsyscall_freebsd_arm64.go | 1 - .../x/sys/unix/zsyscall_freebsd_riscv64.go | 1 - .../x/sys/unix/zsyscall_illumos_amd64.go | 1 - .../golang.org/x/sys/unix/zsyscall_linux.go | 11 +- .../x/sys/unix/zsyscall_linux_386.go | 1 - .../x/sys/unix/zsyscall_linux_amd64.go | 1 - .../x/sys/unix/zsyscall_linux_arm.go | 1 - .../x/sys/unix/zsyscall_linux_arm64.go | 1 - .../x/sys/unix/zsyscall_linux_loong64.go | 1 - .../x/sys/unix/zsyscall_linux_mips.go | 1 - .../x/sys/unix/zsyscall_linux_mips64.go | 1 - .../x/sys/unix/zsyscall_linux_mips64le.go | 1 - .../x/sys/unix/zsyscall_linux_mipsle.go | 1 - .../x/sys/unix/zsyscall_linux_ppc.go | 1 - .../x/sys/unix/zsyscall_linux_ppc64.go | 1 - .../x/sys/unix/zsyscall_linux_ppc64le.go | 1 - .../x/sys/unix/zsyscall_linux_riscv64.go | 1 - .../x/sys/unix/zsyscall_linux_s390x.go | 1 - .../x/sys/unix/zsyscall_linux_sparc64.go | 1 - .../x/sys/unix/zsyscall_netbsd_386.go | 1 - .../x/sys/unix/zsyscall_netbsd_amd64.go | 1 - .../x/sys/unix/zsyscall_netbsd_arm.go | 1 - .../x/sys/unix/zsyscall_netbsd_arm64.go | 1 - .../x/sys/unix/zsyscall_openbsd_386.go | 46 +- .../x/sys/unix/zsyscall_openbsd_386.s | 15 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 46 +- .../x/sys/unix/zsyscall_openbsd_amd64.s | 15 + .../x/sys/unix/zsyscall_openbsd_arm.go | 46 +- .../x/sys/unix/zsyscall_openbsd_arm.s | 15 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 46 +- .../x/sys/unix/zsyscall_openbsd_arm64.s | 15 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 46 +- .../x/sys/unix/zsyscall_openbsd_mips64.s | 15 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 46 +- .../x/sys/unix/zsyscall_openbsd_ppc64.s | 18 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 46 +- .../x/sys/unix/zsyscall_openbsd_riscv64.s | 15 + .../x/sys/unix/zsyscall_solaris_amd64.go | 1 - .../x/sys/unix/zsyscall_zos_s390x.go | 1 - .../x/sys/unix/zsysctl_openbsd_386.go | 1 - .../x/sys/unix/zsysctl_openbsd_amd64.go | 1 - .../x/sys/unix/zsysctl_openbsd_arm.go | 1 - .../x/sys/unix/zsysctl_openbsd_arm64.go | 1 - .../x/sys/unix/zsysctl_openbsd_mips64.go | 1 - .../x/sys/unix/zsysctl_openbsd_ppc64.go | 1 - .../x/sys/unix/zsysctl_openbsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_darwin_amd64.go | 1 - .../x/sys/unix/zsysnum_darwin_arm64.go | 1 - .../x/sys/unix/zsysnum_dragonfly_amd64.go | 1 - .../x/sys/unix/zsysnum_freebsd_386.go | 1 - .../x/sys/unix/zsysnum_freebsd_amd64.go | 1 - .../x/sys/unix/zsysnum_freebsd_arm.go | 1 - .../x/sys/unix/zsysnum_freebsd_arm64.go | 1 - .../x/sys/unix/zsysnum_freebsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_linux_386.go | 2 +- .../x/sys/unix/zsysnum_linux_amd64.go | 3 +- .../x/sys/unix/zsysnum_linux_arm.go | 2 +- .../x/sys/unix/zsysnum_linux_arm64.go | 2 +- .../x/sys/unix/zsysnum_linux_loong64.go | 2 +- .../x/sys/unix/zsysnum_linux_mips.go | 2 +- .../x/sys/unix/zsysnum_linux_mips64.go | 2 +- .../x/sys/unix/zsysnum_linux_mips64le.go | 2 +- .../x/sys/unix/zsysnum_linux_mipsle.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc64.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc64le.go | 2 +- .../x/sys/unix/zsysnum_linux_riscv64.go | 2 +- .../x/sys/unix/zsysnum_linux_s390x.go | 2 +- .../x/sys/unix/zsysnum_linux_sparc64.go | 2 +- .../x/sys/unix/zsysnum_netbsd_386.go | 1 - .../x/sys/unix/zsysnum_netbsd_amd64.go | 1 - .../x/sys/unix/zsysnum_netbsd_arm.go | 1 - .../x/sys/unix/zsysnum_netbsd_arm64.go | 1 - .../x/sys/unix/zsysnum_openbsd_386.go | 1 - .../x/sys/unix/zsysnum_openbsd_amd64.go | 1 - .../x/sys/unix/zsysnum_openbsd_arm.go | 1 - .../x/sys/unix/zsysnum_openbsd_arm64.go | 1 - .../x/sys/unix/zsysnum_openbsd_mips64.go | 1 - .../x/sys/unix/zsysnum_openbsd_ppc64.go | 1 - .../x/sys/unix/zsysnum_openbsd_riscv64.go | 1 - .../x/sys/unix/zsysnum_zos_s390x.go | 1 - .../golang.org/x/sys/unix/ztypes_aix_ppc.go | 1 - .../golang.org/x/sys/unix/ztypes_aix_ppc64.go | 1 - .../x/sys/unix/ztypes_darwin_amd64.go | 1 - .../x/sys/unix/ztypes_darwin_arm64.go | 1 - .../x/sys/unix/ztypes_dragonfly_amd64.go | 1 - .../x/sys/unix/ztypes_freebsd_386.go | 1 - .../x/sys/unix/ztypes_freebsd_amd64.go | 1 - .../x/sys/unix/ztypes_freebsd_arm.go | 1 - .../x/sys/unix/ztypes_freebsd_arm64.go | 1 - .../x/sys/unix/ztypes_freebsd_riscv64.go | 1 - vendor/golang.org/x/sys/unix/ztypes_linux.go | 13 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 1 - .../x/sys/unix/ztypes_linux_amd64.go | 1 - .../golang.org/x/sys/unix/ztypes_linux_arm.go | 1 - .../x/sys/unix/ztypes_linux_arm64.go | 1 - .../x/sys/unix/ztypes_linux_loong64.go | 1 - .../x/sys/unix/ztypes_linux_mips.go | 1 - .../x/sys/unix/ztypes_linux_mips64.go | 1 - .../x/sys/unix/ztypes_linux_mips64le.go | 1 - .../x/sys/unix/ztypes_linux_mipsle.go | 1 - .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 1 - .../x/sys/unix/ztypes_linux_ppc64.go | 1 - .../x/sys/unix/ztypes_linux_ppc64le.go | 1 - .../x/sys/unix/ztypes_linux_riscv64.go | 1 - .../x/sys/unix/ztypes_linux_s390x.go | 1 - .../x/sys/unix/ztypes_linux_sparc64.go | 1 - .../x/sys/unix/ztypes_netbsd_386.go | 1 - .../x/sys/unix/ztypes_netbsd_amd64.go | 1 - .../x/sys/unix/ztypes_netbsd_arm.go | 1 - .../x/sys/unix/ztypes_netbsd_arm64.go | 1 - .../x/sys/unix/ztypes_openbsd_386.go | 1 - .../x/sys/unix/ztypes_openbsd_amd64.go | 1 - .../x/sys/unix/ztypes_openbsd_arm.go | 1 - .../x/sys/unix/ztypes_openbsd_arm64.go | 1 - .../x/sys/unix/ztypes_openbsd_mips64.go | 1 - .../x/sys/unix/ztypes_openbsd_ppc64.go | 1 - .../x/sys/unix/ztypes_openbsd_riscv64.go | 1 - .../x/sys/unix/ztypes_solaris_amd64.go | 1 - .../golang.org/x/sys/unix/ztypes_zos_s390x.go | 1 - vendor/golang.org/x/sys/windows/aliases.go | 1 - vendor/golang.org/x/sys/windows/empty.s | 1 - vendor/golang.org/x/sys/windows/eventlog.go | 1 - vendor/golang.org/x/sys/windows/mksyscall.go | 1 - vendor/golang.org/x/sys/windows/race.go | 1 - vendor/golang.org/x/sys/windows/race0.go | 1 - .../golang.org/x/sys/windows/registry/key.go | 1 - .../x/sys/windows/registry/mksyscall.go | 1 - .../x/sys/windows/registry/syscall.go | 1 - .../x/sys/windows/registry/value.go | 1 - vendor/golang.org/x/sys/windows/service.go | 1 - vendor/golang.org/x/sys/windows/str.go | 1 - vendor/golang.org/x/sys/windows/syscall.go | 1 - .../x/sys/windows/syscall_windows.go | 4 +- .../golang.org/x/sys/windows/types_windows.go | 28 +- .../x/sys/windows/zsyscall_windows.go | 9 + vendor/golang.org/x/term/term_unix.go | 1 - vendor/golang.org/x/term/term_unix_bsd.go | 1 - vendor/golang.org/x/term/term_unix_other.go | 1 - vendor/golang.org/x/term/term_unsupported.go | 1 - .../x/text/secure/bidirule/bidirule10.0.0.go | 1 - .../x/text/secure/bidirule/bidirule9.0.0.go | 1 - .../x/text/unicode/bidi/tables10.0.0.go | 1 - .../x/text/unicode/bidi/tables11.0.0.go | 1 - .../x/text/unicode/bidi/tables12.0.0.go | 1 - .../x/text/unicode/bidi/tables13.0.0.go | 1 - .../x/text/unicode/bidi/tables15.0.0.go | 1 - .../x/text/unicode/bidi/tables9.0.0.go | 1 - .../x/text/unicode/norm/tables10.0.0.go | 1 - .../x/text/unicode/norm/tables11.0.0.go | 1 - .../x/text/unicode/norm/tables12.0.0.go | 1 - .../x/text/unicode/norm/tables13.0.0.go | 1 - .../x/text/unicode/norm/tables15.0.0.go | 1 - .../x/text/unicode/norm/tables9.0.0.go | 1 - .../golang.org/x/text/width/tables10.0.0.go | 1 - .../golang.org/x/text/width/tables11.0.0.go | 1 - .../golang.org/x/text/width/tables12.0.0.go | 1 - .../golang.org/x/text/width/tables13.0.0.go | 1 - .../golang.org/x/text/width/tables15.0.0.go | 1 - vendor/golang.org/x/text/width/tables9.0.0.go | 1 - .../x/tools/cmd/stringer/stringer.go | 5 +- .../x/tools/go/ast/inspector/inspector.go | 4 +- .../x/tools/go/buildutil/fakecontext.go | 3 +- .../x/tools/go/buildutil/overlay.go | 3 +- .../golang.org/x/tools/go/internal/cgo/cgo.go | 3 +- .../tools/go/internal/packagesdriver/sizes.go | 24 +- vendor/golang.org/x/tools/go/packages/doc.go | 2 +- .../golang.org/x/tools/go/packages/golist.go | 93 +- .../x/tools/go/packages/golist_overlay.go | 492 --------- .../x/tools/go/packages/packages.go | 183 ++-- .../x/tools/go/types/objectpath/objectpath.go | 752 ++++++++++++++ vendor/golang.org/x/tools/imports/forward.go | 4 +- .../x/tools/internal/event/tag/tag.go | 2 +- .../x/tools/internal/fastwalk/fastwalk.go | 196 ---- .../internal/fastwalk/fastwalk_darwin.go | 119 --- .../fastwalk/fastwalk_dirent_fileno.go | 14 - .../internal/fastwalk/fastwalk_dirent_ino.go | 15 - .../fastwalk/fastwalk_dirent_namlen_bsd.go | 14 - .../fastwalk/fastwalk_dirent_namlen_linux.go | 29 - .../internal/fastwalk/fastwalk_portable.go | 38 - .../tools/internal/fastwalk/fastwalk_unix.go | 153 --- .../x/tools/internal/gcimporter/gcimporter.go | 3 +- .../x/tools/internal/gcimporter/iexport.go | 175 +++- .../x/tools/internal/gcimporter/iimport.go | 188 +++- .../x/tools/internal/gocommand/invoke.go | 28 +- .../x/tools/internal/gopathwalk/walk.go | 141 ++- .../x/tools/internal/imports/fix.go | 7 +- .../x/tools/internal/imports/mod.go | 13 +- .../x/tools/internal/imports/mod_cache.go | 2 +- .../x/tools/internal/imports/zstdlib.go | 230 +++++ .../x/tools/internal/typeparams/common.go | 26 + .../x/tools/internal/typeparams/coretype.go | 8 +- .../x/tools/internal/typeparams/termlist.go | 2 +- .../internal/typeparams/typeparams_go117.go | 2 +- .../internal/typeparams/typeparams_go118.go | 2 +- .../x/tools/internal/typeparams/typeterm.go | 9 +- vendor/modules.txt | 40 +- .../pkg/client/fake/client.go | 933 ++++++++++++++++++ .../controller-runtime/pkg/client/fake/doc.go | 38 + 600 files changed, 7351 insertions(+), 2427 deletions(-) create mode 100644 pkg/grpcManager/mock/client_mock.go create mode 100644 pkg/k8ObjManager/mock/manager_mock.go create mode 100644 pkg/loadRequest/loadHttp/http_suite_test.go create mode 100644 pkg/loadRequest/loadHttp/http_test.go create mode 100644 pkg/logger/logger_suite_test.go create mode 100644 pkg/logger/logger_test.go create mode 100644 pkg/reportManager/worker_test.go create mode 100644 pkg/resource/resource_suite_test.go create mode 100644 pkg/resource/resource_test.go create mode 100644 pkg/runningTask/runningTask_suite_test.go create mode 100644 pkg/runningTask/runningTask_test.go create mode 100644 pkg/scheduler/cachedb_test.go create mode 100644 pkg/scheduler/runtime/runtime_suite_test.go create mode 100644 pkg/scheduler/runtime/runtime_test.go create mode 100644 pkg/scheduler/schedule_test.go create mode 100644 pkg/scheduler/scheduler_suite_test.go create mode 100644 pkg/scheduler/tracing_test.go create mode 100644 pkg/taskStatusManager/date_suite_test.go create mode 100644 pkg/taskStatusManager/date_test.go delete mode 100644 pkg/utils/utils.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/LICENSE create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/README.md create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/patch.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s create mode 100644 vendor/github.com/golang/mock/AUTHORS create mode 100644 vendor/github.com/golang/mock/CONTRIBUTORS create mode 100644 vendor/github.com/golang/mock/LICENSE create mode 100644 vendor/github.com/golang/mock/gomock/call.go create mode 100644 vendor/github.com/golang/mock/gomock/callset.go create mode 100644 vendor/github.com/golang/mock/gomock/controller.go create mode 100644 vendor/github.com/golang/mock/gomock/matchers.go delete mode 100644 vendor/golang.org/x/net/http2/go111.go delete mode 100644 vendor/golang.org/x/net/http2/go115.go delete mode 100644 vendor/golang.org/x/net/http2/go118.go delete mode 100644 vendor/golang.org/x/net/http2/not_go111.go delete mode 100644 vendor/golang.org/x/net/http2/not_go115.go delete mode 100644 vendor/golang.org/x/net/http2/not_go118.go create mode 100644 vendor/golang.org/x/tools/go/types/objectpath/objectpath.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go delete mode 100644 vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go create mode 100644 vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go create mode 100644 vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go diff --git a/go.mod b/go.mod index 9a8f5e39..490eeb37 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/zap v1.25.0 - golang.org/x/net v0.17.0 + golang.org/x/net v0.18.0 google.golang.org/grpc v1.58.0 google.golang.org/protobuf v1.31.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 @@ -61,7 +61,9 @@ require ( ) require ( + github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/docker/docker v24.0.5+incompatible + github.com/golang/mock v1.6.0 github.com/shirou/gopsutil v3.21.11+incompatible k8s.io/apiserver v0.26.3 sigs.k8s.io/yaml v1.3.0 @@ -160,15 +162,15 @@ require ( go.etcd.io/etcd/client/v3 v3.5.9 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.10.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/mod v0.14.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/sync v0.5.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.3 // indirect + golang.org/x/tools v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect diff --git a/go.sum b/go.sum index 5c38dc4f..456bfa6f 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,8 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agiledragon/gomonkey/v2 v2.11.0 h1:5oxSgA+tC1xuGsrIorR+sYiziYltmJyEZ9qA25b6l5U= +github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= @@ -234,6 +236,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -303,6 +307,7 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= @@ -339,6 +344,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 h1:VzM3TYHDgqPkettiP6I6q2jOeQFL4nrJM+UcAc4f6Fs= github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= @@ -460,6 +466,8 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= @@ -589,8 +597,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -626,8 +634,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/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.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -666,8 +674,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -693,8 +701,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -747,13 +755,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220908164124-27713097b956/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.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.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.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.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -764,8 +772,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/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.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -777,6 +785,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -825,10 +834,11 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= 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= diff --git a/pkg/fileManager/fileManager_suite_test.go b/pkg/fileManager/fileManager_suite_test.go index ca2152a1..285458f2 100644 --- a/pkg/fileManager/fileManager_suite_test.go +++ b/pkg/fileManager/fileManager_suite_test.go @@ -9,9 +9,9 @@ import ( . "github.com/onsi/gomega" ) -func TestIppoolCR(t *testing.T) { +func TestFileManager(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "logger Suite") + RunSpecs(t, "file manager Suite") } var _ = BeforeSuite(func() { diff --git a/pkg/fileManager/manager.go b/pkg/fileManager/manager.go index eb473fcd..91555b1a 100644 --- a/pkg/fileManager/manager.go +++ b/pkg/fileManager/manager.go @@ -13,10 +13,8 @@ import ( ) type FileManager interface { - RemoveTaskFiles(kindName string, taskName string) error WriteTaskFile(kindName string, taskName string, roundNumber int, nodeName string, endTime time.Time, data []byte) error GetTaskAllFile(kindName string, taskName string) ([]string, error) - GetAllFile() ([]string, error) CheckTaskFileExisted(kindName string, taskName string, roundNumber int) bool } @@ -119,22 +117,6 @@ func GenerateTaskFileName(kindName string, taskName string, roundNumber int, nod return fmt.Sprintf("%s_%s_round%d_%s_%s", kindName, taskName, roundNumber, nodeName, suffix) } -func (s *fileManager) GetAllFile() ([]string, error) { - filelist, e := os.ReadDir(s.reportDir) - if e != nil { - return nil, fmt.Errorf("failed to read directory %s, error=%v", s.reportDir, e) - } - - fileList := []string{} - for _, item := range filelist { - if item.IsDir() { - continue - } - fileList = append(fileList, path.Join(s.reportDir, item.Name())) - } - return fileList, nil -} - func (s *fileManager) GetTaskAllFile(kindName string, taskName string) ([]string, error) { filelist, e := os.ReadDir(s.reportDir) if e != nil { @@ -174,29 +156,6 @@ func (s *fileManager) CheckTaskFileExisted(kindName string, taskName string, rou return false } -func (s *fileManager) RemoveTaskFiles(kindName string, taskName string) error { - v, e := s.GetTaskAllFile(kindName, taskName) - if e != nil { - return e - } - - failList := []string{} - for _, m := range v { - if e := os.RemoveAll(m); e != nil { - failList = append(failList, m) - s.logger.Sugar().Errorf("failed to remove file %v for kind %v task %v, error=%v", m, kindName, taskName, e) - } else { - s.logger.Sugar().Info("remove file %v for kind %v task %v", m, kindName, taskName) - } - } - - if len(failList) > 0 { - return fmt.Errorf("failed to remove files: %v", failList) - } - return nil - -} - func (s *fileManager) WriteTaskFile(kindName string, taskName string, roundNumber int, nodeName string, endTime time.Time, data []byte) error { name := GenerateTaskFileName(kindName, taskName, roundNumber, nodeName, endTime) diff --git a/pkg/fileManager/manager_test.go b/pkg/fileManager/manager_test.go index c7f19d2e..4da7a726 100644 --- a/pkg/fileManager/manager_test.go +++ b/pkg/fileManager/manager_test.go @@ -5,17 +5,18 @@ package fileManager_test import ( "fmt" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" "os" "path" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/kdoctor-io/kdoctor/pkg/fileManager" "github.com/kdoctor-io/kdoctor/pkg/logger" "time" ) -var _ = Describe("test ippool CR", Label("ippoolCR"), Pending, func() { +var _ = Describe("test fileManager", Label("fileManager"), func() { var reportDir string @@ -45,11 +46,11 @@ var _ = Describe("test ippool CR", Label("ippoolCR"), Pending, func() { expectedFilePath := path.Join(reportDir, expectedFileName) GinkgoWriter.Printf("expect file %v \n", expectedFilePath) - filelist, e := f.GetAllFile() + filelist, e := f.GetTaskAllFile(kindName, taskName) Expect(e).NotTo(HaveOccurred(), "failed to read directory %s, error=%v", reportDir, e) Expect(len(filelist)).To(Equal(1)) Expect(filelist).To(ConsistOf([]string{expectedFilePath})) - + Expect(f.CheckTaskFileExisted(kindName, taskName, roundNumber)).To(BeTrue()) // read data readdata, er := os.ReadFile(expectedFilePath) Expect(er).NotTo(HaveOccurred(), "failed to read file %s, error=%v", expectedFilePath, er) @@ -57,7 +58,7 @@ var _ = Describe("test ippool CR", Label("ippoolCR"), Pending, func() { // ---- check deletion time.Sleep(10 * time.Second) - filelist, e = f.GetAllFile() + filelist, e = f.GetTaskAllFile(kindName, taskName) Expect(e).NotTo(HaveOccurred(), "failed to read directory , error=%v", e) Expect(len(filelist)).To(Equal(0)) diff --git a/pkg/fileManager/tools_test.go b/pkg/fileManager/tools_test.go index 8b88e86f..fa9f63a8 100644 --- a/pkg/fileManager/tools_test.go +++ b/pkg/fileManager/tools_test.go @@ -8,11 +8,12 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("test ippool CR", Label("ippoolCR"), func() { +var _ = Describe("test fileManager tool", Label("fileManager"), func() { It("test basic", func() { filePath := "/tmp/_loggertest/a.txt" + fileManager.DefaultFileWriter(100, 0, 0) wr := fileManager.NewFileWriter(filePath) GinkgoWriter.Printf("succeed to new write for %v", filePath) defer wr.Close() diff --git a/pkg/grpcManager/mock/client_mock.go b/pkg/grpcManager/mock/client_mock.go new file mode 100644 index 00000000..e406ea85 --- /dev/null +++ b/pkg/grpcManager/mock/client_mock.go @@ -0,0 +1,83 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by MockGen. DO NOT EDIT. +// Source: ./client.go + +// Package grpcManager is a generated GoMock package. +package grpcManager + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + agentGrpc "github.com/kdoctor-io/kdoctor/api/v1/agentGrpc" +) + +// MockGrpcClientManager is a mock of GrpcClientManager interface. +type MockGrpcClientManager struct { + ctrl *gomock.Controller + recorder *MockGrpcClientManagerMockRecorder +} + +// MockGrpcClientManagerMockRecorder is the mock recorder for MockGrpcClientManager. +type MockGrpcClientManagerMockRecorder struct { + mock *MockGrpcClientManager +} + +// NewMockGrpcClientManager creates a new mock instance. +func NewMockGrpcClientManager(ctrl *gomock.Controller) *MockGrpcClientManager { + mock := &MockGrpcClientManager{ctrl: ctrl} + mock.recorder = &MockGrpcClientManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGrpcClientManager) EXPECT() *MockGrpcClientManagerMockRecorder { + return m.recorder +} + +// GetFileList mocks base method. +func (m *MockGrpcClientManager) GetFileList(ctx context.Context, serverAddress, directory string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFileList", ctx, serverAddress, directory) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFileList indicates an expected call of GetFileList. +func (mr *MockGrpcClientManagerMockRecorder) GetFileList(ctx, serverAddress, directory interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileList", reflect.TypeOf((*MockGrpcClientManager)(nil).GetFileList), ctx, serverAddress, directory) +} + +// SaveRemoteFileToLocal mocks base method. +func (m *MockGrpcClientManager) SaveRemoteFileToLocal(ctx context.Context, serverAddress, remoteFilePath, localFilePath string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveRemoteFileToLocal", ctx, serverAddress, remoteFilePath, localFilePath) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveRemoteFileToLocal indicates an expected call of SaveRemoteFileToLocal. +func (mr *MockGrpcClientManagerMockRecorder) SaveRemoteFileToLocal(ctx, serverAddress, remoteFilePath, localFilePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveRemoteFileToLocal", reflect.TypeOf((*MockGrpcClientManager)(nil).SaveRemoteFileToLocal), ctx, serverAddress, remoteFilePath, localFilePath) +} + +// SendRequestForExecRequest mocks base method. +func (m *MockGrpcClientManager) SendRequestForExecRequest(ctx context.Context, serverAddress []string, requestList []*agentGrpc.ExecRequestMsg) ([]*agentGrpc.ExecResponseMsg, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRequestForExecRequest", ctx, serverAddress, requestList) + ret0, _ := ret[0].([]*agentGrpc.ExecResponseMsg) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRequestForExecRequest indicates an expected call of SendRequestForExecRequest. +func (mr *MockGrpcClientManagerMockRecorder) SendRequestForExecRequest(ctx, serverAddress, requestList interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRequestForExecRequest", reflect.TypeOf((*MockGrpcClientManager)(nil).SendRequestForExecRequest), ctx, serverAddress, requestList) +} diff --git a/pkg/k8ObjManager/mock/manager_mock.go b/pkg/k8ObjManager/mock/manager_mock.go new file mode 100644 index 00000000..561483c3 --- /dev/null +++ b/pkg/k8ObjManager/mock/manager_mock.go @@ -0,0 +1,385 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by MockGen. DO NOT EDIT. +// Source: ./manager.go + +// Package k8sObjManager is a generated GoMock package. +package k8sObjManager + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + k8sObjManager "github.com/kdoctor-io/kdoctor/pkg/k8ObjManager" + v1 "k8s.io/api/apps/v1" + v10 "k8s.io/api/core/v1" + v11 "k8s.io/api/networking/v1" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockK8sObjManager is a mock of K8sObjManager interface. +type MockK8sObjManager struct { + ctrl *gomock.Controller + recorder *MockK8sObjManagerMockRecorder +} + +// MockK8sObjManagerMockRecorder is the mock recorder for MockK8sObjManager. +type MockK8sObjManagerMockRecorder struct { + mock *MockK8sObjManager +} + +// NewMockK8sObjManager creates a new mock instance. +func NewMockK8sObjManager(ctrl *gomock.Controller) *MockK8sObjManager { + mock := &MockK8sObjManager{ctrl: ctrl} + mock.recorder = &MockK8sObjManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockK8sObjManager) EXPECT() *MockK8sObjManagerMockRecorder { + return m.recorder +} + +// GetConfigMap mocks base method. +func (m *MockK8sObjManager) GetConfigMap(ctx context.Context, name, namespace string) (*v10.ConfigMap, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetConfigMap", ctx, name, namespace) + ret0, _ := ret[0].(*v10.ConfigMap) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetConfigMap indicates an expected call of GetConfigMap. +func (mr *MockK8sObjManagerMockRecorder) GetConfigMap(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigMap", reflect.TypeOf((*MockK8sObjManager)(nil).GetConfigMap), ctx, name, namespace) +} + +// GetDaemonset mocks base method. +func (m *MockK8sObjManager) GetDaemonset(ctx context.Context, name, namespace string) (*v1.DaemonSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDaemonset", ctx, name, namespace) + ret0, _ := ret[0].(*v1.DaemonSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDaemonset indicates an expected call of GetDaemonset. +func (mr *MockK8sObjManagerMockRecorder) GetDaemonset(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDaemonset", reflect.TypeOf((*MockK8sObjManager)(nil).GetDaemonset), ctx, name, namespace) +} + +// GetDeployment mocks base method. +func (m *MockK8sObjManager) GetDeployment(ctx context.Context, name, namespace string) (*v1.Deployment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDeployment", ctx, name, namespace) + ret0, _ := ret[0].(*v1.Deployment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDeployment indicates an expected call of GetDeployment. +func (mr *MockK8sObjManagerMockRecorder) GetDeployment(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockK8sObjManager)(nil).GetDeployment), ctx, name, namespace) +} + +// GetIngress mocks base method. +func (m *MockK8sObjManager) GetIngress(ctx context.Context, name, namespace string) (*v11.Ingress, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIngress", ctx, name, namespace) + ret0, _ := ret[0].(*v11.Ingress) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetIngress indicates an expected call of GetIngress. +func (mr *MockK8sObjManagerMockRecorder) GetIngress(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIngress", reflect.TypeOf((*MockK8sObjManager)(nil).GetIngress), ctx, name, namespace) +} + +// GetNode mocks base method. +func (m *MockK8sObjManager) GetNode(ctx context.Context, nodeName string) (*v10.Node, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNode", ctx, nodeName) + ret0, _ := ret[0].(*v10.Node) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNode indicates an expected call of GetNode. +func (mr *MockK8sObjManagerMockRecorder) GetNode(ctx, nodeName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNode", reflect.TypeOf((*MockK8sObjManager)(nil).GetNode), ctx, nodeName) +} + +// GetNodeIP mocks base method. +func (m *MockK8sObjManager) GetNodeIP(ctx context.Context, nodeName string) (string, string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNodeIP", ctx, nodeName) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetNodeIP indicates an expected call of GetNodeIP. +func (mr *MockK8sObjManagerMockRecorder) GetNodeIP(ctx, nodeName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNodeIP", reflect.TypeOf((*MockK8sObjManager)(nil).GetNodeIP), ctx, nodeName) +} + +// GetPodList mocks base method. +func (m *MockK8sObjManager) GetPodList(ctx context.Context, opts ...client.ListOption) ([]v10.Pod, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetPodList", varargs...) + ret0, _ := ret[0].([]v10.Pod) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPodList indicates an expected call of GetPodList. +func (mr *MockK8sObjManagerMockRecorder) GetPodList(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPodList", reflect.TypeOf((*MockK8sObjManager)(nil).GetPodList), varargs...) +} + +// GetSecret mocks base method. +func (m *MockK8sObjManager) GetSecret(ctx context.Context, name, namespace string) (*v10.Secret, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSecret", ctx, name, namespace) + ret0, _ := ret[0].(*v10.Secret) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSecret indicates an expected call of GetSecret. +func (mr *MockK8sObjManagerMockRecorder) GetSecret(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecret", reflect.TypeOf((*MockK8sObjManager)(nil).GetSecret), ctx, name, namespace) +} + +// GetService mocks base method. +func (m *MockK8sObjManager) GetService(ctx context.Context, name, namespace string) (*v10.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetService", ctx, name, namespace) + ret0, _ := ret[0].(*v10.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetService indicates an expected call of GetService. +func (mr *MockK8sObjManagerMockRecorder) GetService(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", reflect.TypeOf((*MockK8sObjManager)(nil).GetService), ctx, name, namespace) +} + +// GetServiceAccessUrl mocks base method. +func (m *MockK8sObjManager) GetServiceAccessUrl(ctx context.Context, name, namespace, portName string) (*k8sObjManager.ServiceAccessUrl, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetServiceAccessUrl", ctx, name, namespace, portName) + ret0, _ := ret[0].(*k8sObjManager.ServiceAccessUrl) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetServiceAccessUrl indicates an expected call of GetServiceAccessUrl. +func (mr *MockK8sObjManagerMockRecorder) GetServiceAccessUrl(ctx, name, namespace, portName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceAccessUrl", reflect.TypeOf((*MockK8sObjManager)(nil).GetServiceAccessUrl), ctx, name, namespace, portName) +} + +// ListDaemonsetPodIPs mocks base method. +func (m *MockK8sObjManager) ListDaemonsetPodIPs(ctx context.Context, daemonsetName, daemonsetNameSpace string) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDaemonsetPodIPs", ctx, daemonsetName, daemonsetNameSpace) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDaemonsetPodIPs indicates an expected call of ListDaemonsetPodIPs. +func (mr *MockK8sObjManagerMockRecorder) ListDaemonsetPodIPs(ctx, daemonsetName, daemonsetNameSpace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDaemonsetPodIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListDaemonsetPodIPs), ctx, daemonsetName, daemonsetNameSpace) +} + +// ListDaemonsetPodMultusIPs mocks base method. +func (m *MockK8sObjManager) ListDaemonsetPodMultusIPs(ctx context.Context, daemonsetName, daemonsetNameSpace string) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDaemonsetPodMultusIPs", ctx, daemonsetName, daemonsetNameSpace) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDaemonsetPodMultusIPs indicates an expected call of ListDaemonsetPodMultusIPs. +func (mr *MockK8sObjManagerMockRecorder) ListDaemonsetPodMultusIPs(ctx, daemonsetName, daemonsetNameSpace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDaemonsetPodMultusIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListDaemonsetPodMultusIPs), ctx, daemonsetName, daemonsetNameSpace) +} + +// ListDaemonsetPodNodes mocks base method. +func (m *MockK8sObjManager) ListDaemonsetPodNodes(ctx context.Context, daemonsetName, daemonsetNameSpace string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDaemonsetPodNodes", ctx, daemonsetName, daemonsetNameSpace) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDaemonsetPodNodes indicates an expected call of ListDaemonsetPodNodes. +func (mr *MockK8sObjManagerMockRecorder) ListDaemonsetPodNodes(ctx, daemonsetName, daemonsetNameSpace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDaemonsetPodNodes", reflect.TypeOf((*MockK8sObjManager)(nil).ListDaemonsetPodNodes), ctx, daemonsetName, daemonsetNameSpace) +} + +// ListDeployPodMultusIPs mocks base method. +func (m *MockK8sObjManager) ListDeployPodMultusIPs(ctx context.Context, deploymentName, deploymentNameSpace string) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDeployPodMultusIPs", ctx, deploymentName, deploymentNameSpace) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDeployPodMultusIPs indicates an expected call of ListDeployPodMultusIPs. +func (mr *MockK8sObjManagerMockRecorder) ListDeployPodMultusIPs(ctx, deploymentName, deploymentNameSpace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDeployPodMultusIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListDeployPodMultusIPs), ctx, deploymentName, deploymentNameSpace) +} + +// ListDeploymentPodIPs mocks base method. +func (m *MockK8sObjManager) ListDeploymentPodIPs(ctx context.Context, deploymentName, deploymentNameSpace string) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDeploymentPodIPs", ctx, deploymentName, deploymentNameSpace) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDeploymentPodIPs indicates an expected call of ListDeploymentPodIPs. +func (mr *MockK8sObjManagerMockRecorder) ListDeploymentPodIPs(ctx, deploymentName, deploymentNameSpace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDeploymentPodIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListDeploymentPodIPs), ctx, deploymentName, deploymentNameSpace) +} + +// ListNodes mocks base method. +func (m *MockK8sObjManager) ListNodes(ctx context.Context, opts ...client.ListOption) (*v10.NodeList, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListNodes", varargs...) + ret0, _ := ret[0].(*v10.NodeList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListNodes indicates an expected call of ListNodes. +func (mr *MockK8sObjManagerMockRecorder) ListNodes(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListNodes", reflect.TypeOf((*MockK8sObjManager)(nil).ListNodes), varargs...) +} + +// ListSelectedNodes mocks base method. +func (m *MockK8sObjManager) ListSelectedNodes(ctx context.Context, labelSelector *v12.LabelSelector) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSelectedNodes", ctx, labelSelector) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSelectedNodes indicates an expected call of ListSelectedNodes. +func (mr *MockK8sObjManagerMockRecorder) ListSelectedNodes(ctx, labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSelectedNodes", reflect.TypeOf((*MockK8sObjManager)(nil).ListSelectedNodes), ctx, labelSelector) +} + +// ListSelectedPod mocks base method. +func (m *MockK8sObjManager) ListSelectedPod(ctx context.Context, labelSelector *v12.LabelSelector) ([]v10.Pod, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSelectedPod", ctx, labelSelector) + ret0, _ := ret[0].([]v10.Pod) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSelectedPod indicates an expected call of ListSelectedPod. +func (mr *MockK8sObjManagerMockRecorder) ListSelectedPod(ctx, labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSelectedPod", reflect.TypeOf((*MockK8sObjManager)(nil).ListSelectedPod), ctx, labelSelector) +} + +// ListSelectedPodIPs mocks base method. +func (m *MockK8sObjManager) ListSelectedPodIPs(ctx context.Context, labelSelector *v12.LabelSelector) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSelectedPodIPs", ctx, labelSelector) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSelectedPodIPs indicates an expected call of ListSelectedPodIPs. +func (mr *MockK8sObjManagerMockRecorder) ListSelectedPodIPs(ctx, labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSelectedPodIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListSelectedPodIPs), ctx, labelSelector) +} + +// ListSelectedPodMultusIPs mocks base method. +func (m *MockK8sObjManager) ListSelectedPodMultusIPs(ctx context.Context, labelSelector *v12.LabelSelector) (k8sObjManager.PodIps, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSelectedPodMultusIPs", ctx, labelSelector) + ret0, _ := ret[0].(k8sObjManager.PodIps) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSelectedPodMultusIPs indicates an expected call of ListSelectedPodMultusIPs. +func (mr *MockK8sObjManagerMockRecorder) ListSelectedPodMultusIPs(ctx, labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSelectedPodMultusIPs", reflect.TypeOf((*MockK8sObjManager)(nil).ListSelectedPodMultusIPs), ctx, labelSelector) +} + +// ListServicesDnsIP mocks base method. +func (m *MockK8sObjManager) ListServicesDnsIP(ctx context.Context) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListServicesDnsIP", ctx) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListServicesDnsIP indicates an expected call of ListServicesDnsIP. +func (mr *MockK8sObjManagerMockRecorder) ListServicesDnsIP(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListServicesDnsIP", reflect.TypeOf((*MockK8sObjManager)(nil).ListServicesDnsIP), ctx) +} + +// MatchNodeSelected mocks base method. +func (m *MockK8sObjManager) MatchNodeSelected(ctx context.Context, nodeName string, labelSelector *v12.LabelSelector) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MatchNodeSelected", ctx, nodeName, labelSelector) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MatchNodeSelected indicates an expected call of MatchNodeSelected. +func (mr *MockK8sObjManagerMockRecorder) MatchNodeSelected(ctx, nodeName, labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MatchNodeSelected", reflect.TypeOf((*MockK8sObjManager)(nil).MatchNodeSelected), ctx, nodeName, labelSelector) +} diff --git a/pkg/loadRequest/loadDns/dns_suite_test.go b/pkg/loadRequest/loadDns/dns_suite_test.go index 7bf6cde5..fec4655f 100644 --- a/pkg/loadRequest/loadDns/dns_suite_test.go +++ b/pkg/loadRequest/loadDns/dns_suite_test.go @@ -11,7 +11,7 @@ import ( config "github.com/kdoctor-io/kdoctor/pkg/types" ) -func TestIppoolCR(t *testing.T) { +func TestLoadDns(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "load request Suite") } diff --git a/pkg/loadRequest/loadHttp/http_suite_test.go b/pkg/loadRequest/loadHttp/http_suite_test.go new file mode 100644 index 00000000..967891ae --- /dev/null +++ b/pkg/loadRequest/loadHttp/http_suite_test.go @@ -0,0 +1,22 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 +package loadHttp_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + config "github.com/kdoctor-io/kdoctor/pkg/types" +) + +func TestLoadHttp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "load request Suite") +} + +var _ = BeforeSuite(func() { + // nothing to do + config.AgentConfig.Configmap.NethttpDefaultConcurrency = 10 +}) diff --git a/pkg/loadRequest/loadHttp/http_test.go b/pkg/loadRequest/loadHttp/http_test.go new file mode 100644 index 00000000..f438b9ae --- /dev/null +++ b/pkg/loadRequest/loadHttp/http_test.go @@ -0,0 +1,69 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package loadHttp_test + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/kdoctor-io/kdoctor/pkg/loadRequest/loadHttp" + "github.com/kdoctor-io/kdoctor/pkg/logger" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("test http ", Label("http"), func() { + header := make(map[string]string, 0) + header["test"] = "test" + + It("test no latency ", func() { + + req := &loadHttp.HttpRequestData{ + Method: "GET", + Url: "https://github.com", + PerRequestTimeoutMS: 10000, + RequestTimeSecond: 10, + Qps: 10, + Header: header, + } + log := logger.NewStdoutLogger("debug", "test") + result := loadHttp.HttpRequest(log, req) + + jsongByte, e := json.Marshal(result) + Expect(e).NotTo(HaveOccurred(), "failed to Marshal , error=%v", e) + + var out bytes.Buffer + e = json.Indent(&out, jsongByte, "", "\t") + Expect(e).NotTo(HaveOccurred(), "failed to Indent , error=%v", e) + fmt.Printf("%s\n", out.String()) + + Expect(len(result.Errors)).To(Equal(0)) + + }) + + It("test latency ", func() { + + req := &loadHttp.HttpRequestData{ + Method: "GET", + Url: "https://github.com", + PerRequestTimeoutMS: 10000, + RequestTimeSecond: 10, + EnableLatencyMetric: true, + Qps: 10, + Header: header, + } + log := logger.NewStdoutLogger("debug", "test") + result := loadHttp.HttpRequest(log, req) + jsongByte, e := json.Marshal(result) + Expect(e).NotTo(HaveOccurred(), "failed to Marshal , error=%v", e) + + var out bytes.Buffer + e = json.Indent(&out, jsongByte, "", "\t") + Expect(e).NotTo(HaveOccurred(), "failed to Indent , error=%v", e) + fmt.Printf("%s\n", out.String()) + + Expect(len(result.Errors)).To(Equal(0)) + + }) +}) diff --git a/pkg/logger/logger_suite_test.go b/pkg/logger/logger_suite_test.go new file mode 100644 index 00000000..5dcb7367 --- /dev/null +++ b/pkg/logger/logger_suite_test.go @@ -0,0 +1,19 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 +package logger_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestNewStdoutLogger(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "logger Suite") +} + +var _ = BeforeSuite(func() { + // nothing to do +}) diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go new file mode 100644 index 00000000..f376ec97 --- /dev/null +++ b/pkg/logger/logger_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package logger_test + +import ( + "github.com/kdoctor-io/kdoctor/pkg/logger" + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("test logger ", Label("logger"), func() { + It("test debug logger ", func() { + logger.NewStdoutLogger("debug", "test") + }) + It("test info logger ", func() { + logger.NewStdoutLogger("info", "test") + }) + It("test warn logger ", func() { + logger.NewStdoutLogger("warn", "test") + }) + It("test error logger ", func() { + logger.NewStdoutLogger("error", "test") + }) + It("test fatal logger ", func() { + logger.NewStdoutLogger("fatal", "test") + }) + It("test panic logger ", func() { + logger.NewStdoutLogger("panic", "test") + }) + It("test empty logger ", func() { + logger.NewStdoutLogger("", "test") + }) +}) diff --git a/pkg/pluginManager/controllerManager.go b/pkg/pluginManager/controllerManager.go index e69d108d..276f2738 100644 --- a/pkg/pluginManager/controllerManager.go +++ b/pkg/pluginManager/controllerManager.go @@ -102,7 +102,7 @@ func (s *pluginManager) RunControllerController(healthPort int, webhookPort int, } } - runtimeDB := make([]scheduler.DB, 0, len(s.chainingPlugins)) + runtimeDB := make(map[string]scheduler.DB, len(s.chainingPlugins)) ctx, cancelFunc := context.WithCancel(context.TODO()) for name, plugin := range s.chainingPlugins { // setup reconcile @@ -120,7 +120,7 @@ func (s *pluginManager) RunControllerController(healthPort int, webhookPort int, SignalTimeOutDuration: time.Duration(types.ControllerConfig.ResourceTrackerSignalTimeoutSeconds) * time.Second, TraceGapDuration: time.Duration(types.ControllerConfig.ResourceTrackerTraceGapSeconds) * time.Second, }, logger.Named(name+"Tracker")) - runtimeDB = append(runtimeDB, tracker.DB) + runtimeDB[name] = tracker.DB k := &pluginControllerReconciler{ logger: logger.Named(name + "Reconciler"), plugin: plugin, diff --git a/pkg/pluginManager/controllerTools.go b/pkg/pluginManager/controllerTools.go index c442606a..a555e885 100644 --- a/pkg/pluginManager/controllerTools.go +++ b/pkg/pluginManager/controllerTools.go @@ -133,7 +133,6 @@ func (s *pluginControllerReconciler) WriteSummaryReport(taskName string, roundNu if !s.fm.CheckTaskFileExisted(kindName, instanceName, roundNumber) { // add to workqueue to collect all report of last round, for node latestRecord.FailedAgentNodeList and latestRecord.SucceedAgentNodeList reportManager.TriggerSyncReport(fmt.Sprintf("%s.%d", taskName, roundNumber)) - // TODO (Icarus9913): change to use v1beta1.Report ? // write controller summary report msg := plugintypes.PluginReport{ diff --git a/pkg/reportManager/manager.go b/pkg/reportManager/manager.go index 94bdefbf..332db135 100644 --- a/pkg/reportManager/manager.go +++ b/pkg/reportManager/manager.go @@ -6,6 +6,7 @@ package reportManager import ( "context" "github.com/kdoctor-io/kdoctor/pkg/scheduler" + "github.com/kdoctor-io/kdoctor/pkg/types" "go.uber.org/zap" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/workqueue" @@ -17,16 +18,18 @@ const ( ) type reportManager struct { - logger *zap.Logger - reportDir string - collectInterval time.Duration - queue workqueue.RateLimitingInterface - runtimeDB []scheduler.DB + logger *zap.Logger + reportDir string + collectInterval time.Duration + queue workqueue.RateLimitingInterface + appHttpHealthyRuntimeDB scheduler.DB + netReachRuntimeDB scheduler.DB + netDNSRuntimeDB scheduler.DB } var globalReportManager *reportManager -func InitReportManager(logger *zap.Logger, reportDir string, collectInterval time.Duration, db []scheduler.DB) { +func InitReportManager(logger *zap.Logger, reportDir string, collectInterval time.Duration, db map[string]scheduler.DB) { if globalReportManager != nil { return } @@ -36,8 +39,19 @@ func InitReportManager(logger *zap.Logger, reportDir string, collectInterval tim reportDir: reportDir, collectInterval: collectInterval, queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "reportManager"), - runtimeDB: db, } + + for k, v := range db { + switch k { + case types.KindNameAppHttpHealthy: + globalReportManager.appHttpHealthyRuntimeDB = v + case types.KindNameNetReach: + globalReportManager.netReachRuntimeDB = v + case types.KindNameNetdns: + globalReportManager.netDNSRuntimeDB = v + } + } + go globalReportManager.runWorker() } @@ -56,18 +70,14 @@ func (s *reportManager) runWorker() { // please do not run more than one worker, or else it races to write reports go wait.UntilWithContext(ctx, s.worker, time.Second) - // periodically trigger sync - for { - TriggerSyncReport("periodicallyTrigger") - <-time.After(s.collectInterval) - } + select {} } -func TriggerSyncReport(triggerName string) { +func TriggerSyncReport(tgt string) { if globalReportManager != nil { - globalReportManager.logger.Sugar().Debugf("trigger to sync agent report from source %v", triggerName) + globalReportManager.logger.Sugar().Debugf("trigger to sync agent report from source %v", tgt) // s.queue.AddRateLimited(triggerName) - globalReportManager.queue.AddAfter(triggerName, 10*time.Second) + globalReportManager.queue.AddAfter(tgt, 10*time.Second) } } diff --git a/pkg/reportManager/manager_test.go b/pkg/reportManager/manager_test.go index 3145dfcf..5cc0fa98 100644 --- a/pkg/reportManager/manager_test.go +++ b/pkg/reportManager/manager_test.go @@ -1,48 +1,81 @@ // Copyright 2023 Authors of kdoctor-io // SPDX-License-Identifier: Apache-2.0 -package reportManager_test +package reportManager import ( + "context" "fmt" + "github.com/agiledragon/gomonkey/v2" + "github.com/golang/mock/gomock" + k8sObjManager "github.com/kdoctor-io/kdoctor/pkg/k8ObjManager" + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/scheduler" + "github.com/kdoctor-io/kdoctor/pkg/types" + "github.com/kdoctor-io/kdoctor/pkg/utils" . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/kdoctor-io/kdoctor/pkg/reportManager" + "time" ) var _ = Describe("unit test", Label("unit test"), func() { + var reportDir string + BeforeEach(func() { + reportDir = fmt.Sprintf("/tmp/_FM_%d", time.Now().Nanosecond()) + }) - It("test getMissRemoteReport", Pending, func() { + It("init InitReportManager ", Label("reportManager"), func() { - name1 := "Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T11:47:08Z" - name2 := "Nethttp_test-agent_round1_kdoctor-control-plane_2022-12-21T11:45:08Z" - remoteFileList := []string{name1, name2} - localFileList := []string{name1} + ctx := context.Background() + // mock + patch := gomonkey.ApplyFuncReturn(utils.GetFileList, []string{"test-report-local-file"}, nil) + defer patch.Reset() - missFile := reportManager.GetMissRemoteReport(remoteFileList, localFileList) - Expect(len(missFile)).To(Equal(1)) - Expect(missFile).To(ConsistOf([]string{name2})) + mockPodIP := k8sObjManager.PodIps{"testPod": []k8sObjManager.IPs{{ + InterfaceName: "eth0", + IPv4: "127.0.0.1", + }}} - }) + k8sManager.EXPECT().ListDeploymentPodIPs(gomock.Eq(ctx), gomock.Eq("deployment"), gomock.Eq("default")).Return(mockPodIP, nil).AnyTimes() + k8sManager.EXPECT().ListDaemonsetPodIPs(gomock.Eq(ctx), gomock.Eq("daemonset"), gomock.Eq("default")).Return(mockPodIP, nil).AnyTimes() + + grpcClient.EXPECT().GetFileList(gomock.Eq(ctx), gomock.Eq("127.0.0.1"), gomock.Eq("/report")).Return([]string{"test-report-local-file"}, nil).AnyTimes() + + log := logger.NewStdoutLogger("debug", "reportManager Test") + dbMap := make(map[string]scheduler.DB, 3) + + appDB := scheduler.NewDB(5, log) + reachDB := scheduler.NewDB(5, log) + dnsDB := scheduler.NewDB(5, log) + + dbMap[types.KindNameAppHttpHealthy] = appDB + dbMap[types.KindNameNetReach] = reachDB + dbMap[types.KindNameNetdns] = dnsDB - It("test2 getMissRemoteReport", func() { + InitReportManager(log, reportDir, time.Second*5, dbMap) - remoteFileList := []string{ - "Nethttp_test-agent_round1_kdoctor-worker_2022-11-21T12:24:08Z", - "Nethttp_test-agent_round2_kdoctor-worker_2022-11-21T12:26:07Z", - } + _ = appDB.Apply(scheduler.BuildItem(v1beta1.TaskResource{ + RuntimeName: "test-app", + RuntimeType: types.KindDeployment, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameAppHttpHealthy, "test-app", nil)) - localFileList := []string{ - "Nethttp_test-agent_round1_kdoctor-control-plane_2022-12-21T12:18:20Z", - "Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T12:18:20Z", - "Nethttp_test-agent_round1_summary_2022-12-21T12:18:05Z", - "Nethttp_test-agent_round2_summary_2022-12-21T12:20:05Z", - } + _ = reachDB.Apply(scheduler.BuildItem(v1beta1.TaskResource{ + RuntimeName: "test-reach", + RuntimeType: types.KindDeployment, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameNetReach, "test-reach", nil)) - missFile := reportManager.GetMissRemoteReport(remoteFileList, localFileList) - fmt.Printf("%v", missFile) + _ = dnsDB.Apply(scheduler.BuildItem(v1beta1.TaskResource{ + RuntimeName: "test-dns", + RuntimeType: types.KindDeployment, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameNetdns, "test-dns", nil)) + TriggerSyncReport("test-app.1") + TriggerSyncReport("test-reach.1") + TriggerSyncReport("test-dns.1") + time.Sleep(time.Second * 20) }) }) diff --git a/pkg/reportManager/reportManager_suite_test.go b/pkg/reportManager/reportManager_suite_test.go index 4c0bcd73..66940606 100644 --- a/pkg/reportManager/reportManager_suite_test.go +++ b/pkg/reportManager/reportManager_suite_test.go @@ -1,19 +1,31 @@ // Copyright 2023 Authors of kdoctor-io // SPDX-License-Identifier: Apache-2.0 -package reportManager_test +package reportManager import ( + "github.com/golang/mock/gomock" + grpcManager_mock "github.com/kdoctor-io/kdoctor/pkg/grpcManager/mock" + k8sObjManager_mock "github.com/kdoctor-io/kdoctor/pkg/k8ObjManager/mock" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) -func TestIppoolCR(t *testing.T) { +var mockCtrl *gomock.Controller +var k8sManager *k8sObjManager_mock.MockK8sObjManager +var grpcClient *grpcManager_mock.MockGrpcClientManager + +func TestReportManager(t *testing.T) { + mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() RegisterFailHandler(Fail) RunSpecs(t, "reportManager Suite") } var _ = BeforeSuite(func() { // nothing to do + k8sManager = k8sObjManager_mock.NewMockK8sObjManager(mockCtrl) + grpcClient = grpcManager_mock.NewMockGrpcClientManager(mockCtrl) + }) diff --git a/pkg/reportManager/worker.go b/pkg/reportManager/worker.go index bdfb01f0..a120db50 100644 --- a/pkg/reportManager/worker.go +++ b/pkg/reportManager/worker.go @@ -82,7 +82,7 @@ func (s *reportManager) syncReportFromOneAgent(ctx context.Context, logger *zap. } -func (s *reportManager) runControllerAggregateReportOnce(ctx context.Context, logger *zap.Logger, taskName string) error { +func (s *reportManager) runControllerAggregateReportOnce(ctx context.Context, logger *zap.Logger, taskKind string, taskName string) error { var task scheduler.Item var err error var podIP k8sObjManager.PodIps @@ -96,15 +96,14 @@ func (s *reportManager) runControllerAggregateReportOnce(ctx context.Context, lo return nil } logger.Sugar().Debugf("before sync, local report files: %v", localFileList) - // get all runtime obj - for _, v := range s.runtimeDB { - task, err = v.Get(taskName) - if err != nil { - logger.Sugar().Debugf(err.Error()) - continue - } else { - break - } + + switch taskKind { + case types.KindNameAppHttpHealthy: + task, err = s.appHttpHealthyRuntimeDB.Get(taskName) + case types.KindNameNetReach: + task, err = s.netReachRuntimeDB.Get(taskName) + case types.KindNameNetdns: + task, err = s.netDNSRuntimeDB.Get(taskName) } if err != nil { return err @@ -155,16 +154,9 @@ func (s *reportManager) runControllerAggregateReportOnce(ctx context.Context, lo } // just one worker to sync all report and save to local disc of controller pod -func (s *reportManager) syncHandler(ctx context.Context, triggerName string) error { +func (s *reportManager) syncHandler(ctx context.Context, trigger string) error { logger := s.logger.With( - zap.String("triggerSource", triggerName), + zap.String("triggerSource", trigger), ) - var taskName string - if triggerName == "periodicallyTrigger" { - taskName = triggerName - } else { - taskName = strings.Split(triggerName, ".")[1] - } - - return s.runControllerAggregateReportOnce(ctx, logger, taskName) + return s.runControllerAggregateReportOnce(ctx, logger, strings.Split(trigger, ".")[0], strings.Split(trigger, ".")[1]) } diff --git a/pkg/reportManager/worker_test.go b/pkg/reportManager/worker_test.go new file mode 100644 index 00000000..204757d2 --- /dev/null +++ b/pkg/reportManager/worker_test.go @@ -0,0 +1,80 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package reportManager + +import ( + "context" + "fmt" + "github.com/agiledragon/gomonkey/v2" + "github.com/golang/mock/gomock" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/util/workqueue" + "time" +) + +var _ = Describe("unit test", Label("unit test "), func() { + var reportDir string + ctx := context.Background() + BeforeEach(func() { + reportDir = fmt.Sprintf("/tmp/_FM_%d", time.Now().Nanosecond()) + }) + + It("test getMissRemoteReport", Label("reportManager worker"), func() { + + name1 := "Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T11:47:08Z" + name2 := "Nethttp_test-agent_round1_kdoctor-control-plane_2022-12-21T11:45:08Z" + remoteFileList := []string{name1, name2} + localFileList := []string{name1} + + missFile := GetMissRemoteReport(remoteFileList, localFileList) + Expect(len(missFile)).To(Equal(1)) + Expect(missFile).To(ConsistOf([]string{name2})) + + }) + + It("test2 getMissRemoteReport", func() { + + remoteFileList := []string{ + "Nethttp_test-agent_round1_kdoctor-worker_2022-11-21T12:24:08Z", + "Nethttp_test-agent_round2_kdoctor-worker_2022-11-21T12:26:07Z", + } + + localFileList := []string{ + "Nethttp_test-agent_round1_kdoctor-control-plane_2022-12-21T12:18:20Z", + "Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T12:18:20Z", + "Nethttp_test-agent_round1_summary_2022-12-21T12:18:05Z", + "Nethttp_test-agent_round2_summary_2022-12-21T12:20:05Z", + } + + missFile := GetMissRemoteReport(remoteFileList, localFileList) + fmt.Printf("%v", missFile) + + }) + + It("syncReportFromOneAgent ", Label("reportManager worker"), func() { + types.ControllerConfig.DirPathAgentReport = "/report" + // mock + patch := gomonkey.ApplyFuncReturn(GetMissRemoteReport, []string{"Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T12:18:20Z"}) + defer patch.Reset() + + grpcClient.EXPECT().GetFileList(gomock.Eq(ctx), gomock.Any(), gomock.Any()).Return([]string{"Nethttp_test-agent_round1_kdoctor-control-plane_2022-12-21T12:18:20Z"}, nil).AnyTimes() + grpcClient.EXPECT().SaveRemoteFileToLocal(gomock.Eq(ctx), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + log := logger.NewStdoutLogger("debug", "reportManager Test") + + rm := &reportManager{ + logger: log, + reportDir: reportDir, + collectInterval: time.Second * 5, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "reportManager"), + } + + rm.syncReportFromOneAgent(ctx, log, grpcClient, []string{"Nethttp_test-agent_round1_kdoctor-worker_2022-12-21T12:18:20Z"}, "test", "127.0.0.1") + + }) + +}) diff --git a/pkg/resource/resource_suite_test.go b/pkg/resource/resource_suite_test.go new file mode 100644 index 00000000..5a1759f6 --- /dev/null +++ b/pkg/resource/resource_suite_test.go @@ -0,0 +1,20 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestResource(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "resource Suite") +} + +var _ = BeforeSuite(func() { + // nothing to do +}) diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go new file mode 100644 index 00000000..c999a1d9 --- /dev/null +++ b/pkg/resource/resource_test.go @@ -0,0 +1,26 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import ( + "context" + . "github.com/onsi/ginkgo/v2" + "time" +) + +var _ = Describe("resource unit test", Label("resource"), func() { + + It("get system resource", func() { + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + r := InitResource(ctx) + r.RunResourceCollector() + time.Sleep(time.Second * 5) + GinkgoWriter.Printf("resource stats %+v", r.Stats()) + time.Sleep(time.Second * 5) + r.Stop() + }) + +}) diff --git a/pkg/runningTask/runningTask_suite_test.go b/pkg/runningTask/runningTask_suite_test.go new file mode 100644 index 00000000..5492e9b1 --- /dev/null +++ b/pkg/runningTask/runningTask_suite_test.go @@ -0,0 +1,20 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package runningTask + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestRunningTask(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "RunningTask Suite") +} + +var _ = BeforeSuite(func() { + // nothing to do +}) diff --git a/pkg/runningTask/runningTask_test.go b/pkg/runningTask/runningTask_test.go new file mode 100644 index 00000000..9fc9b306 --- /dev/null +++ b/pkg/runningTask/runningTask_test.go @@ -0,0 +1,35 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package runningTask + +import ( + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("runningTask unit test", Label("runningTask"), func() { + + It("running task", func() { + var load v1beta1.TotalRunningLoad + rt := InitRunningTask() + rt.SetTask(Task{Kind: types.KindNameAppHttpHealthy, Qps: 10, Name: "test1"}) + rt.SetTask(Task{Kind: types.KindNameNetdns, Qps: 20, Name: "test2"}) + rt.SetTask(Task{Kind: types.KindNameNetReach, Qps: 30, Name: "test3"}) + load = rt.QpsStats() + + Expect(load.AppHttpHealthyQPS).To(Equal(int64(10)), "appHttpHealthy qps not equal") + Expect(load.NetReachQPS).To(Equal(int64(30)), "netReach qps not equal") + Expect(load.NetDnsQPS).To(Equal(int64(20)), "netDns qps not equal") + + rt.DeleteTask("test1") + load = rt.QpsStats() + Expect(load.AppHttpHealthyQPS).To(Equal(int64(0)), "appHttpHealthy qps not equal") + Expect(load.NetReachQPS).To(Equal(int64(30)), "netReach qps not equal") + Expect(load.NetDnsQPS).To(Equal(int64(20)), "netDns qps not equal") + + }) + +}) diff --git a/pkg/scheduler/cachedb.go b/pkg/scheduler/cachedb.go index 00b0554e..4ec14a58 100644 --- a/pkg/scheduler/cachedb.go +++ b/pkg/scheduler/cachedb.go @@ -84,30 +84,22 @@ func BuildItem(resource crd.TaskResource, taskKind, taskName string, deletionTim func (d *Database) Apply(item Item) error { d.Lock() - + defer d.Unlock() old, ok := d.cache[item.RuntimeKey] if !ok { if len(d.cache) == d.maxCap { - d.Unlock() return fmt.Errorf("database is out of capacity, discard item %v", item) } - d.cache[item.RuntimeKey] = item - d.Unlock() d.log.Sugar().Debugf("create item %v", item) - return nil } else { if !reflect.DeepEqual(old, item) { item.Task.Join(old.Task) d.cache[item.RuntimeKey] = item - d.Unlock() d.log.Sugar().Debugf("item %v has changed, the old one is %v, and the new one is %v", item.RuntimeKey, old, item) - return nil } } - - d.Unlock() return nil } @@ -130,6 +122,7 @@ func (d *Database) Delete(item Item) { if !ok { d.Unlock() d.log.Sugar().Debugf("item %v already deleted", item.RuntimeKey) + return } delete(d.cache, item.RuntimeKey) diff --git a/pkg/scheduler/cachedb_test.go b/pkg/scheduler/cachedb_test.go new file mode 100644 index 00000000..69b2fc9a --- /dev/null +++ b/pkg/scheduler/cachedb_test.go @@ -0,0 +1,89 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package scheduler + +import ( + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" +) + +var _ = Describe("cacheDB unit test", Label("cacheDB"), func() { + + It("cacheDB", Label("cacheDB"), func() { + db := NewDB(30, logger.NewStdoutLogger("debug", "cacheDB")) + taskName := "test" + runtimeName := TaskRuntimeName(types.KindNameAppHttpHealthy, taskName) + serviceV4Name := TaskRuntimeServiceName(runtimeName, corev1.IPv4Protocol) + serviceV6Name := TaskRuntimeServiceName(runtimeName, corev1.IPv6Protocol) + item := BuildItem(v1beta1.TaskResource{ + RuntimeName: runtimeName, + RuntimeType: types.KindDeployment, + ServiceNameV4: &serviceV4Name, + ServiceNameV6: &serviceV6Name, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameAppHttpHealthy, taskName, nil) + + err := db.Apply(item) + Expect(err).To(BeNil(), "apply db") + + runtimeList := db.List() + Expect(len(runtimeList)).To(Equal(1), "runtime list") + + runtimeGet, err := db.Get(taskName) + Expect(err).To(BeNil(), "get runtime") + Expect(runtimeGet.RuntimeName).To(Equal(item.RuntimeName), "get runtime") + Expect(runtimeGet.RuntimeKind).To(Equal(item.RuntimeKind), "get runtime") + Expect(runtimeGet.RuntimeKey).To(Equal(item.RuntimeKey), "get runtime") + Expect(runtimeGet.RuntimeStatus).To(Equal(item.RuntimeStatus), "get runtime") + Expect(runtimeGet.ServiceNameV4).To(Equal(item.ServiceNameV4), "get runtime") + Expect(runtimeGet.ServiceNameV6).To(Equal(item.ServiceNameV6), "get runtime") + Expect(runtimeGet.RuntimeDeletionTime).To(Equal(item.RuntimeDeletionTime), "get runtime") + + Expect(len(db.List())).To(Equal(1), "delete item") + + taskName2 := "test2" + runtimeName2 := TaskRuntimeName(types.KindNameAppHttpHealthy, taskName2) + serviceV4Name2 := TaskRuntimeServiceName(runtimeName2, corev1.IPv4Protocol) + serviceV6Name2 := TaskRuntimeServiceName(runtimeName2, corev1.IPv6Protocol) + item2 := BuildItem(v1beta1.TaskResource{ + RuntimeName: runtimeName2, + RuntimeType: types.KindDeployment, + ServiceNameV4: &serviceV4Name2, + ServiceNameV6: &serviceV6Name2, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameAppHttpHealthy, taskName2, nil) + + err = db.Apply(item2) + Expect(err).To(BeNil(), "apply other item") + db.Delete(item) + db.Delete(item) + Expect(len(db.List())).To(Equal(1), "delete item") + + item3 := BuildItem(v1beta1.TaskResource{ + RuntimeName: runtimeName2, + RuntimeType: types.KindDeployment, + ServiceNameV4: &serviceV4Name2, + ServiceNameV6: &serviceV6Name2, + RuntimeStatus: v1beta1.RuntimeCreated, + }, types.KindNameNetReach, taskName2, nil) + err = db.Apply(item3) + Expect(err).To(BeNil(), "apply other item") + Expect(len(db.List())).To(Equal(1), "len item") + + _ = NewDB(0, logger.NewStdoutLogger("debug", "cacheDB")) + db3 := NewDB(1, logger.NewStdoutLogger("debug", "cacheDB")) + err = db3.Apply(item2) + Expect(err).To(BeNil(), "apply other item") + err = db3.Apply(item) + Expect(err).NotTo(BeNil(), "apply other item") + + _, err = db3.Get(taskName) + Expect(err).NotTo(BeNil(), "get not exist task") + }) + +}) diff --git a/pkg/scheduler/runtime/runtime_suite_test.go b/pkg/scheduler/runtime/runtime_suite_test.go new file mode 100644 index 00000000..1ca306b8 --- /dev/null +++ b/pkg/scheduler/runtime/runtime_suite_test.go @@ -0,0 +1,34 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package runtime + +import ( + networkingv1 "k8s.io/api/networking/v1" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestRuntimeTask(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Runtime Suite") +} + +var c client.WithWatch + +var _ = BeforeSuite(func() { + scheme := runtime.NewScheme() + err := networkingv1.AddToScheme(scheme) + Expect(err).To(BeNil(), "add ingress scheme failed") + err = clientgoscheme.AddToScheme(scheme) + Expect(err).To(BeNil(), "add client go scheme failed") + builder := fake.NewClientBuilder().WithScheme(scheme) + c = builder.Build() + +}) diff --git a/pkg/scheduler/runtime/runtime_test.go b/pkg/scheduler/runtime/runtime_test.go new file mode 100644 index 00000000..b1c216c8 --- /dev/null +++ b/pkg/scheduler/runtime/runtime_test.go @@ -0,0 +1,74 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package runtime + +import ( + "context" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("runtime unit test", Label("runtime"), func() { + + It("runtime task daemonSet", func() { + name := "testDaemonSet" + namespace := "testDaemonSet" + ctx := context.Background() + runtimeDaemonSet := NewDaemonSetRuntime(c, c, namespace, name, logger.NewStdoutLogger("debug", "runtime daemonSet")) + + ds := types.DaemonsetTempl.DeepCopy() + ds.SetName(name) + ds.SetNamespace(namespace) + + err := c.Create(ctx, ds) + Expect(err).To(BeNil(), "create daemonSet failed") + + ready := runtimeDaemonSet.IsReady(ctx) + Expect(ready).To(BeTrue(), "not ready daemonset ") + err = runtimeDaemonSet.Delete(ctx) + Expect(err).To(BeNil(), "delete daemonSet failed") + + runtimeDaemonSetNotFound := NewDaemonSetRuntime(c, c, "not-found", "not-found", logger.NewStdoutLogger("debug", "runtime daemonSet")) + readyNotFound := runtimeDaemonSetNotFound.IsReady(ctx) + Expect(readyNotFound).To(BeFalse(), "not ready daemonset ") + err = runtimeDaemonSetNotFound.Delete(ctx) + Expect(err).To(BeNil(), "not found daemonSet") + _, err = FindRuntime(c, c, types.KindDaemonSet, namespace, logger.NewStdoutLogger("debug", "runtime daemonSet")) + Expect(err).To(BeNil(), "get runtime") + + }) + + It("runtime task deployment", func() { + name := "testDeployment" + namespace := "testDeployment" + ctx := context.Background() + runtimeDeploy := NewDeploymentRuntime(c, c, namespace, name, logger.NewStdoutLogger("debug", "runtime deploy")) + + deploy := types.DeploymentTempl.DeepCopy() + deploy.SetName(name) + deploy.SetNamespace(namespace) + + err := c.Create(ctx, deploy) + Expect(err).To(BeNil(), "create deployment failed") + + runtimeDeploy.IsReady(ctx) + err = runtimeDeploy.Delete(ctx) + Expect(err).To(BeNil(), "delete deployment failed") + + runtimeDeploymentNotFound := NewDeploymentRuntime(c, c, "not-found", "not-found", logger.NewStdoutLogger("debug", "runtime daemonSet")) + readyNotFound := runtimeDeploymentNotFound.IsReady(ctx) + Expect(readyNotFound).To(BeFalse(), "not ready deployment ") + err = runtimeDeploymentNotFound.Delete(ctx) + Expect(err).To(BeNil(), "not found deployment") + _, err = FindRuntime(c, c, types.KindDeployment, namespace, logger.NewStdoutLogger("debug", "runtime daemonSet")) + Expect(err).To(BeNil(), "get runtime") + }) + + It("FindRuntime test ", Label("runtime"), func() { + _, err := FindRuntime(c, c, "test", "test", logger.NewStdoutLogger("debug", "runtime")) + Expect(err).NotTo(BeNil(), "unrecognized runtime type") + }) +}) diff --git a/pkg/scheduler/schedule_test.go b/pkg/scheduler/schedule_test.go new file mode 100644 index 00000000..c8e395ca --- /dev/null +++ b/pkg/scheduler/schedule_test.go @@ -0,0 +1,93 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 +package scheduler + +import ( + "context" + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" +) + +var _ = Describe("schedule unit test", Label("schedule"), func() { + + It("schedule appHttpHealthy", Label("schedule"), func() { + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + tgm := int64(1) + ctx := context.Background() + taskName := "test" + task := &v1beta1.AppHttpHealthy{} + replicas := int32(2) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDeployment, + DeploymentReplicas: &replicas, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: true, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + schedule := NewScheduler(c, c, types.KindNameAppHttpHealthy, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + _, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exist runtime") + }) + + It("schedule netReach", Label("schedule"), func() { + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + tgm := int64(1) + ctx := context.Background() + taskName := "test" + task := &v1beta1.NetReach{} + enabelIngress := true + + target := new(v1beta1.NetReachTarget) + target.Ingress = &enabelIngress + task.Spec.Target = target + + schedule := NewScheduler(c, c, types.KindNameNetReach, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDaemonSet, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: false, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + _, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exists runtime") + }) + + It("schedule unrecognized type", Label("schedule"), func() { + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + tgm := int64(1) + ctx := context.Background() + taskName := "test" + task := &v1beta1.NetReach{} + enabelIngress := true + + target := new(v1beta1.NetReachTarget) + target.Ingress = &enabelIngress + task.Spec.Target = target + + schedule := NewScheduler(c, c, types.KindNameNetReach, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + agentSpec := v1beta1.AgentSpec{ + Kind: "test", + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: false, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + _, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).NotTo(BeNil(), "create not exists runtime,unrecognized type") + }) +}) diff --git a/pkg/scheduler/scheduler_suite_test.go b/pkg/scheduler/scheduler_suite_test.go new file mode 100644 index 00000000..5ac977fb --- /dev/null +++ b/pkg/scheduler/scheduler_suite_test.go @@ -0,0 +1,80 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package scheduler + +import ( + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/types" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestScheduler(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "scheduler Suite") +} + +var c client.WithWatch + +var _ = BeforeSuite(func() { + scheme := runtime.NewScheme() + err := networkingv1.AddToScheme(scheme) + Expect(err).To(BeNil(), "add ingress scheme failed") + err = clientgoscheme.AddToScheme(scheme) + Expect(err).To(BeNil(), "add client go scheme failed") + err = v1beta1.AddToScheme(scheme) + Expect(err).To(BeNil(), "add kdoctor scheme failed") + builder := fake.NewClientBuilder().WithScheme(scheme) + c = builder.Build() + + // workload template + // ingress + ingress := new(networkingv1.Ingress) + rule := networkingv1.IngressRule{} + path := networkingv1.HTTPIngressPath{} + backend := networkingv1.IngressBackend{} + backend.Service = new(networkingv1.IngressServiceBackend) + path.Backend = backend + rule.HTTP = new(networkingv1.HTTPIngressRuleValue) + rule.HTTP.Paths = append(rule.HTTP.Paths, path) + ingress.Spec.Rules = append([]networkingv1.IngressRule{}, rule) + types.IngressTempl = ingress + + // service + service := new(corev1.Service) + service.SetLabels(map[string]string{"test": "test"}) + service.Spec.Selector = map[string]string{"test": "test"} + types.ServiceTempl = service + + // pod + pod := new(corev1.Pod) + container := corev1.Container{Name: "test"} + pod.Spec.Containers = []corev1.Container{container} + types.PodTempl = pod + + // deployment + deployment := new(appsv1.Deployment) + deployment.SetLabels(map[string]string{"test": "test"}) + deployment.Spec.Selector = &v1.LabelSelector{} + replicas := int32(1) + deployment.Spec.Replicas = &replicas + deployment.Spec.Template.Spec.HostNetwork = true + types.DeploymentTempl = deployment + + // daemonSet + daemonSet := new(appsv1.DaemonSet) + daemonSet.SetLabels(map[string]string{"test": "test"}) + daemonSet.Spec.Selector = &v1.LabelSelector{} + types.DaemonsetTempl = daemonSet +}) diff --git a/pkg/scheduler/tracing_test.go b/pkg/scheduler/tracing_test.go new file mode 100644 index 00000000..35743a17 --- /dev/null +++ b/pkg/scheduler/tracing_test.go @@ -0,0 +1,229 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 +package scheduler + +import ( + "context" + "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" + "github.com/kdoctor-io/kdoctor/pkg/logger" + "github.com/kdoctor-io/kdoctor/pkg/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" +) + +var _ = Describe("schedule tracing unit test", Label("schedule"), func() { + + It("schedule tracing appHttpHealthy", Label("schedule"), func() { + config := TrackerConfig{ + ItemChannelBuffer: 500, + MaxDatabaseCap: 5000, + ExecutorWorkers: 3, + SignalTimeOutDuration: time.Second * 3, + TraceGapDuration: time.Second * 10, + } + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + types.ControllerConfig.PodNamespace = "default" + track := NewTracker(c, c, config, logger.NewStdoutLogger("debug", "tracker")) + tgm := int64(1) + ctx := context.Background() + taskName := "test-apphttp" + task := &v1beta1.AppHttpHealthy{} + task.SetName(taskName) + replicas := int32(1) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDeployment, + DeploymentReplicas: &replicas, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: true, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + task.Spec.AgentSpec = &agentSpec + err := c.Create(ctx, task) + Expect(err).To(BeNil(), "create task") + + schedule := NewScheduler(c, c, types.KindNameAppHttpHealthy, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + resource, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exist runtime") + deleteTime := metav1.NewTime(metav1.Now().Add(time.Second * 1)) + item := BuildItem(resource, types.KindNameAppHttpHealthy, taskName, deleteTime.DeepCopy()) + err = track.DB.Apply(item) + Expect(err).To(BeNil(), "track db apply") + + task.Status.Resource = &resource + err = c.Update(ctx, task) + Expect(err).To(BeNil(), "update resource") + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + time.Sleep(time.Second * 2) + track.Start(ctx) + time.Sleep(time.Second * 15) + }) + + It("schedule tracing netReach", Label("schedule"), func() { + config := TrackerConfig{ + ItemChannelBuffer: 500, + MaxDatabaseCap: 5000, + ExecutorWorkers: 3, + SignalTimeOutDuration: time.Second * 3, + TraceGapDuration: time.Second * 10, + } + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + types.ControllerConfig.PodNamespace = "default" + track := NewTracker(c, c, config, logger.NewStdoutLogger("debug", "tracker")) + tgm := int64(1) + ctx := context.Background() + taskName := "test-netreach" + task := &v1beta1.NetReach{} + enable := true + task.Spec.Target = &v1beta1.NetReachTarget{ + Ingress: &enable, + IPv4: &enable, + IPv6: &enable, + ClusterIP: &enable, + NodePort: &enable, + MultusInterface: &enable, + LoadBalancer: &enable, + } + task.SetName(taskName) + replicas := int32(1) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDeployment, + DeploymentReplicas: &replicas, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: true, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + task.Spec.AgentSpec = &agentSpec + err := c.Create(ctx, task) + Expect(err).To(BeNil(), "create task") + + schedule := NewScheduler(c, c, types.KindNameNetReach, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + resource, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exist runtime") + deleteTime := metav1.NewTime(metav1.Now().Add(time.Second * 1)) + item := BuildItem(resource, types.KindNameNetReach, taskName, deleteTime.DeepCopy()) + err = track.DB.Apply(item) + Expect(err).To(BeNil(), "track db apply") + + task.Status.Resource = &resource + err = c.Update(ctx, task) + Expect(err).To(BeNil(), "update resource") + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + time.Sleep(time.Second * 2) + track.Start(ctx) + time.Sleep(time.Second * 15) + }) + + It("schedule tracing netDNS", Label("schedule"), func() { + config := TrackerConfig{ + ItemChannelBuffer: 500, + MaxDatabaseCap: 5000, + ExecutorWorkers: 3, + SignalTimeOutDuration: time.Second * 3, + TraceGapDuration: time.Second * 10, + } + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + types.ControllerConfig.PodNamespace = "default" + track := NewTracker(c, c, config, logger.NewStdoutLogger("debug", "tracker")) + tgm := int64(1) + ctx := context.Background() + taskName := "test-dns" + task := &v1beta1.Netdns{} + task.SetName(taskName) + replicas := int32(1) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDeployment, + DeploymentReplicas: &replicas, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: true, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + task.Spec.AgentSpec = &agentSpec + err := c.Create(ctx, task) + Expect(err).To(BeNil(), "create task") + + schedule := NewScheduler(c, c, types.KindNameNetdns, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + resource, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exist runtime") + deleteTime := metav1.NewTime(metav1.Now().Add(time.Second * 1)) + item := BuildItem(resource, types.KindNameNetdns, taskName, deleteTime.DeepCopy()) + err = track.DB.Apply(item) + Expect(err).To(BeNil(), "track db apply") + + task.Status.Resource = &resource + err = c.Update(ctx, task) + Expect(err).To(BeNil(), "update resource") + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + time.Sleep(time.Second * 2) + track.Start(ctx) + time.Sleep(time.Second * 15) + }) + + It("schedule tracing appHttpHealthy update resource status", Label("schedule"), func() { + config := TrackerConfig{ + ItemChannelBuffer: 500, + MaxDatabaseCap: 5000, + ExecutorWorkers: 3, + SignalTimeOutDuration: time.Second * 3, + TraceGapDuration: time.Second * 10, + } + types.ControllerConfig.Configmap.EnableIPv4 = true + types.ControllerConfig.Configmap.EnableIPv6 = true + types.ControllerConfig.PodNamespace = "default" + track := NewTracker(c, c, config, logger.NewStdoutLogger("debug", "tracker")) + tgm := int64(1) + ctx := context.Background() + taskName := "runtime-apphttp" + task := &v1beta1.AppHttpHealthy{} + task.SetName(taskName) + replicas := int32(1) + agentSpec := v1beta1.AgentSpec{ + Kind: types.KindDeployment, + DeploymentReplicas: &replicas, + Affinity: &v1.Affinity{}, + Env: []v1.EnvVar{{Name: "test", Value: "test"}}, + HostNetwork: true, + Resources: &v1.ResourceRequirements{}, + Annotation: map[string]string{"test": "test"}, + TerminationGracePeriodMinutes: &tgm, + } + task.Spec.AgentSpec = &agentSpec + err := c.Create(ctx, task) + Expect(err).To(BeNil(), "create task") + + schedule := NewScheduler(c, c, types.KindNameAppHttpHealthy, taskName, "", logger.NewStdoutLogger("debug", "schedule")) + resource, err := schedule.CreateTaskRuntimeIfNotExist(ctx, task, agentSpec) + Expect(err).To(BeNil(), "create not exist runtime") + deleteTime := metav1.NewTime(metav1.Now().Add(time.Second * 1)) + item := BuildItem(resource, types.KindNameAppHttpHealthy, taskName, deleteTime.DeepCopy()) + err = track.DB.Apply(item) + Expect(err).To(BeNil(), "track db apply") + + task.Status.Resource = &resource + task.Status.Resource.RuntimeStatus = v1beta1.RuntimeCreating + err = c.Update(ctx, task) + Expect(err).To(BeNil(), "update resource") + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + time.Sleep(time.Second * 2) + track.Start(ctx) + time.Sleep(time.Second * 15) + }) +}) diff --git a/pkg/taskStatusManager/date_suite_test.go b/pkg/taskStatusManager/date_suite_test.go new file mode 100644 index 00000000..47db7204 --- /dev/null +++ b/pkg/taskStatusManager/date_suite_test.go @@ -0,0 +1,19 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 +package taskStatusManager_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestTaskStatusManager(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "task status Suite") +} + +var _ = BeforeSuite(func() { + // nothing to do +}) diff --git a/pkg/taskStatusManager/date_test.go b/pkg/taskStatusManager/date_test.go new file mode 100644 index 00000000..d7cac2eb --- /dev/null +++ b/pkg/taskStatusManager/date_test.go @@ -0,0 +1,18 @@ +// Copyright 2023 Authors of kdoctor-io +// SPDX-License-Identifier: Apache-2.0 + +package taskStatusManager_test + +import ( + "github.com/kdoctor-io/kdoctor/pkg/taskStatusManager" + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("test task status", Label("task status"), func() { + It("test status ", func() { + taskManager := taskStatusManager.NewTaskStatus() + taskManager.SetTask("test", taskStatusManager.RoundStatusOngoing) + taskManager.CheckTask("test") + taskManager.DeleteTask("test") + }) +}) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go deleted file mode 100644 index 14be9b28..00000000 --- a/pkg/utils/utils.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 Authors of kdoctor-io -// SPDX-License-Identifier: Apache-2.0 -package utils - -import ( - "fmt" - "strings" -) - -func GetObjNameNamespace(ns string) (name, namespace string, err error) { - - s := strings.Split(ns, "/") - - if len(s) != 2 { - err = fmt.Errorf("failed get name and namespace from %s ", ns) - return - } - name = s[1] - namespace = s[0] - return -} diff --git a/test/e2e/netdns/netdns_test.go b/test/e2e/netdns/netdns_test.go index 7820d2ec..479fc913 100644 --- a/test/e2e/netdns/netdns_test.go +++ b/test/e2e/netdns/netdns_test.go @@ -23,6 +23,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { successMean := int64(1500) crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() + // netdns netDns := new(v1beta1.Netdns) netDns.Name = netDnsName diff --git a/vendor/github.com/agiledragon/gomonkey/v2/LICENSE b/vendor/github.com/agiledragon/gomonkey/v2/LICENSE new file mode 100644 index 00000000..d75dc90e --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Zhang Xiaolong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/agiledragon/gomonkey/v2/README.md b/vendor/github.com/agiledragon/gomonkey/v2/README.md new file mode 100644 index 00000000..a69177e8 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/README.md @@ -0,0 +1,54 @@ +# gomonkey + +gomonkey is a library to make monkey patching in unit tests easy, and the core idea of monkey patching comes from [Bouke](https://github.com/bouk), you can read [this blogpost](https://bou.ke/blog/monkey-patching-in-go/) for an explanation on how it works. + +## Features + ++ support a patch for a function ++ support a patch for a public member method ++ support a patch for a private member method ++ support a patch for a interface ++ support a patch for a function variable ++ support a patch for a global variable ++ support patches of a specified sequence for a function ++ support patches of a specified sequence for a member method ++ support patches of a specified sequence for a interface ++ support patches of a specified sequence for a function variable + +## Notes ++ gomonkey fails to patch a function or a member method if inlining is enabled, please running your tests with inlining disabled by adding the command line argument that is `-gcflags=-l`(below go1.10) or `-gcflags=all=-l`(go1.10 and above). ++ A panic may happen when a goroutine is patching a function or a member method that is visited by another goroutine at the same time. That is to say, gomonkey is not threadsafe. + +## Supported Platform: + +- ARCH + - amd64 + - arm64 + - 386 + - loong64 + +- OS + - Linux + - MAC OS X + - Windows + +## Installation +- below v2.1.0, for example v2.0.2 +```go +$ go get github.com/agiledragon/gomonkey@v2.0.2 +``` +- v2.1.0 and above, for example v2.2.0 +```go +$ go get github.com/agiledragon/gomonkey/v2@v2.2.0 +``` + +## Test Method +```go +$ cd test +$ go test -gcflags=all=-l +``` + +## Using gomonkey + +Please refer to the test cases as idioms, very complete and detailed. + diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go new file mode 100644 index 00000000..68a7e750 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go @@ -0,0 +1,39 @@ +//go:build go1.17 +// +build go1.17 + +package creflect + +import ( + "unsafe" +) + +// name is an encoded type name with optional extra data. +type name struct { + bytes *byte +} + +func (n name) data(off int, whySafe string) *byte { + return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe)) +} + +func (n name) readVarint(off int) (int, int) { + v := 0 + for i := 0; ; i++ { + x := *n.data(off+i, "read varint") + v += int(x&0x7f) << (7 * i) + if x&0x80 == 0 { + return i + 1, v + } + } +} + +func (n name) name() (s string) { + if n.bytes == nil { + return + } + i, l := n.readVarint(1) + hdr := (*String)(unsafe.Pointer(&s)) + hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string")) + hdr.Len = l + return +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go new file mode 100644 index 00000000..1fb6988a --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go @@ -0,0 +1,25 @@ +//go:build !go1.17 +// +build !go1.17 + +package creflect + +import ( + "unsafe" +) + +// name is an encoded type name with optional extra data. +type name struct { + bytes *byte +} + +func (n name) name() (s string) { + if n.bytes == nil { + return + } + b := (*[4]byte)(unsafe.Pointer(n.bytes)) + + hdr := (*String)(unsafe.Pointer(&s)) + hdr.Data = unsafe.Pointer(&b[3]) + hdr.Len = int(b[1])<<8 | int(b[2]) + return s +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go new file mode 100644 index 00000000..7df1f092 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go @@ -0,0 +1,179 @@ +// Customized reflect package for gomonkey,copy most code from go/src/reflect/type.go + +package creflect + +import ( + "reflect" + "unsafe" +) + +// rtype is the common implementation of most values. +// rtype must be kept in sync with ../runtime/type.go:/^type._type. +type rtype struct { + size uintptr + ptrdata uintptr // number of bytes in the type that can contain pointers + hash uint32 // hash of type; avoids computation in hash tables + tflag tflag // extra type information flags + align uint8 // alignment of variable with this type + fieldAlign uint8 // alignment of struct field with this type + kind uint8 // enumeration for C + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer) bool + gcdata *byte // garbage collection data + str nameOff // string form + ptrToThis typeOff // type for pointer to this type, may be zero +} + +func Create(t reflect.Type) *rtype { + i := *(*funcValue)(unsafe.Pointer(&t)) + r := (*rtype)(i.p) + return r +} + +type funcValue struct { + _ uintptr + p unsafe.Pointer +} + +func funcPointer(v reflect.Method, ok bool) (unsafe.Pointer, bool) { + return (*funcValue)(unsafe.Pointer(&v.Func)).p, ok +} +func MethodByName(r reflect.Type, name string) (fn unsafe.Pointer, ok bool) { + t := Create(r) + if r.Kind() == reflect.Interface { + return funcPointer(r.MethodByName(name)) + } + ut := t.uncommon(r) + if ut == nil { + return nil, false + } + + for _, p := range ut.methods() { + if t.nameOff(p.name).name() == name { + return t.Method(p), true + } + } + return nil, false +} + +func (t *rtype) Method(p method) (fn unsafe.Pointer) { + tfn := t.textOff(p.tfn) + fn = unsafe.Pointer(&tfn) + return +} + +type tflag uint8 +type nameOff int32 // offset to a name +type typeOff int32 // offset to an *rtype +type textOff int32 // offset from top of text section + +//go:linkname resolveTextOff reflect.resolveTextOff +func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +func (t *rtype) textOff(off textOff) unsafe.Pointer { + return resolveTextOff(unsafe.Pointer(t), int32(off)) +} + +//go:linkname resolveNameOff reflect.resolveNameOff +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + +func (t *rtype) nameOff(off nameOff) name { + return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + +const ( + tflagUncommon tflag = 1 << 0 +) + +// uncommonType is present only for defined types or types with methods +type uncommonType struct { + pkgPath nameOff // import path; empty for built-in types like int, string + mcount uint16 // number of methods + xcount uint16 // number of exported methods + moff uint32 // offset from this uncommontype to [mcount]method + _ uint32 // unused +} + +// ptrType represents a pointer type. +type ptrType struct { + rtype + elem *rtype // pointer element (pointed at) type +} + +// funcType represents a function type. +type funcType struct { + rtype + inCount uint16 + outCount uint16 // top bit is set if last input parameter is ... +} + +func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// interfaceType represents an interface type. +type interfaceType struct { + rtype + pkgPath name // import path + methods []imethod // sorted by hash +} + +type imethod struct { + name nameOff // name of method + typ typeOff // .(*FuncType) underneath +} + +type String struct { + Data unsafe.Pointer + Len int +} + +func (t *rtype) uncommon(r reflect.Type) *uncommonType { + if t.tflag&tflagUncommon == 0 { + return nil + } + switch r.Kind() { + case reflect.Ptr: + type u struct { + ptrType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Func: + type u struct { + funcType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Interface: + type u struct { + interfaceType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Struct: + type u struct { + interfaceType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + default: + return nil + } +} + +// Method on non-interface type +type method struct { + name nameOff // name of method + mtyp typeOff // method type (without receiver) + ifn textOff // fn used in interface call (one-word receiver) + tfn textOff // fn used for normal method call +} + +func (t *uncommonType) methods() []method { + if t.mcount == 0 { + return nil + } + return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.mcount > 0"))[:t.mcount:t.mcount] +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go new file mode 100644 index 00000000..27c8d4f2 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go @@ -0,0 +1,13 @@ +package gomonkey + +func buildJmpDirective(double uintptr) []byte { + d0 := byte(double) + d1 := byte(double >> 8) + d2 := byte(double >> 16) + d3 := byte(double >> 24) + + return []byte{ + 0xBA, d0, d1, d2, d3, // MOV edx, double + 0xFF, 0x22, // JMP [edx] + } +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go new file mode 100644 index 00000000..02c1c42c --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go @@ -0,0 +1,18 @@ +package gomonkey + +func buildJmpDirective(double uintptr) []byte { + d0 := byte(double) + d1 := byte(double >> 8) + d2 := byte(double >> 16) + d3 := byte(double >> 24) + d4 := byte(double >> 32) + d5 := byte(double >> 40) + d6 := byte(double >> 48) + d7 := byte(double >> 56) + + return []byte{ + 0x48, 0xBA, d0, d1, d2, d3, d4, d5, d6, d7, // MOV rdx, double + 0xFF, 0x22, // JMP [rdx] + } +} + diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go new file mode 100644 index 00000000..772aa0c3 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go @@ -0,0 +1,37 @@ +//go:build arm64 +// +build arm64 + +package gomonkey + +import "unsafe" + +func buildJmpDirective(double uintptr) []byte { + res := make([]byte, 0, 24) + d0d1 := double & 0xFFFF + d2d3 := double >> 16 & 0xFFFF + d4d5 := double >> 32 & 0xFFFF + d6d7 := double >> 48 & 0xFFFF + + res = append(res, movImm(0b10, 0, d0d1)...) // MOVZ x26, double[16:0] + res = append(res, movImm(0b11, 1, d2d3)...) // MOVK x26, double[32:16] + res = append(res, movImm(0b11, 2, d4d5)...) // MOVK x26, double[48:32] + res = append(res, movImm(0b11, 3, d6d7)...) // MOVK x26, double[64:48] + res = append(res, []byte{0x4A, 0x03, 0x40, 0xF9}...) // LDR x10, [x26] + res = append(res, []byte{0x40, 0x01, 0x1F, 0xD6}...) // BR x10 + + return res +} + +func movImm(opc, shift int, val uintptr) []byte { + var m uint32 = 26 // rd + m |= uint32(val) << 5 // imm16 + m |= uint32(shift&3) << 21 // hw + m |= 0b100101 << 23 // const + m |= uint32(opc&0x3) << 29 // opc + m |= 0b1 << 31 // sf + + res := make([]byte, 4) + *(*uint32)(unsafe.Pointer(&res[0])) = m + + return res +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go new file mode 100644 index 00000000..628dc4f5 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go @@ -0,0 +1,73 @@ +//go:build loong64 +// +build loong64 + +package gomonkey + +import "unsafe" + +const ( + REG_R0 uint32 = 0 + REG_R29 = 29 + REG_R30 = 30 +) + +const ( + OP_ORI uint32 = 0x00E << 22 + OP_LU12IW = 0x00A << 25 + OP_LU32ID = 0x00B << 25 + OP_LU52ID = 0x00C << 22 + OP_LDD = 0x0A3 << 22 + OP_JIRL = 0x013 << 26 +) + +func buildJmpDirective(double uintptr) []byte { + res := make([]byte, 0, 24) + + bit11_0 := (double >> 0) & 0xFFF + bit31_12 := (double >> 12) & 0xFFFFF + bit51_32 := (double >> 32) & 0xFFFFF + bit63_52 := (double >> 52) & 0xFFF + + // lu12i.w r29, bit31_12 + // ori r29, r29, bit11_0 + // lu32i.d r29, bit51_32 + // lu52i.d r29, bit63_52 + // ld.d, r30, r29, 0 + // jirl r0, r30, 0 + res = append(res, wireup_opc(OP_LU12IW, REG_R29, 0, bit31_12)...) + res = append(res, wireup_opc(OP_ORI, REG_R29, REG_R29, bit11_0)...) + res = append(res, wireup_opc(OP_LU32ID, REG_R29, 0, bit51_32)...) + res = append(res, wireup_opc(OP_LU52ID, REG_R29, REG_R29, bit63_52)...) + res = append(res, wireup_opc(OP_LDD, REG_R30, REG_R29, 0)...) + res = append(res, wireup_opc(OP_JIRL, REG_R0, REG_R30, 0)...) + + return res +} + +func wireup_opc(opc uint32, rd, rj uint32, val uintptr) []byte { + var m uint32 = 0 + + switch opc { + case OP_ORI, OP_LU52ID, OP_LDD: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (rj & 0x1F) << 5 // rj + m |= (uint32(val) & 0xFFF) << 10 // si12 + + case OP_LU12IW, OP_LU32ID: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (uint32(val) & 0xFFFFF) << 5 // si20 + + case OP_JIRL: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (rj & 0x1F) << 5 // rj + m |= (uint32(val) & 0xFFFF) << 10 // si16 + } + + res := make([]byte, 4) + *(*uint32)(unsafe.Pointer(&res[0])) = m + + return res +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go new file mode 100644 index 00000000..478bbec8 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go @@ -0,0 +1,24 @@ +package gomonkey + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" +) + +func PtrOf(val []byte) uintptr { + return (*reflect.SliceHeader)(unsafe.Pointer(&val)).Data +} + +func modifyBinary(target uintptr, bytes []byte) { + targetPage := pageStart(target) + res := write(target, PtrOf(bytes), len(bytes), targetPage, syscall.Getpagesize(), syscall.PROT_READ|syscall.PROT_EXEC) + if res != 0 { + panic(fmt.Errorf("failed to write memory, code %v", res)) + } +} + +//go:cgo_import_dynamic mach_task_self mach_task_self "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic mach_vm_protect mach_vm_protect "/usr/lib/libSystem.B.dylib" +func write(target, data uintptr, len int, page uintptr, pageSize, oriProt int) int diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go new file mode 100644 index 00000000..6c169888 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go @@ -0,0 +1,27 @@ +package gomonkey + +import "syscall" + +func modifyBinary(target uintptr, bytes []byte) { + function := entryAddress(target, len(bytes)) + err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC) + if err != nil { + panic(err) + } + copy(function, bytes) + err = mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_EXEC) + if err != nil { + panic(err) + } +} + +func mprotectCrossPage(addr uintptr, length int, prot int) error { + pageSize := syscall.Getpagesize() + for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) { + page := entryAddress(p, pageSize) + if err := syscall.Mprotect(page, prot); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go new file mode 100644 index 00000000..ef0dbc75 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go @@ -0,0 +1,25 @@ +package gomonkey + +import ( + "syscall" + "unsafe" +) + +func modifyBinary(target uintptr, bytes []byte) { + function := entryAddress(target, len(bytes)) + + proc := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") + const PROT_READ_WRITE = 0x40 + var old uint32 + result, _, _ := proc.Call(target, uintptr(len(bytes)), uintptr(PROT_READ_WRITE), uintptr(unsafe.Pointer(&old))) + if result == 0 { + panic(result) + } + copy(function, bytes) + + var ignore uint32 + result, _, _ = proc.Call(target, uintptr(len(bytes)), uintptr(old), uintptr(unsafe.Pointer(&ignore))) + if result == 0 { + panic(result) + } +} \ No newline at end of file diff --git a/vendor/github.com/agiledragon/gomonkey/v2/patch.go b/vendor/github.com/agiledragon/gomonkey/v2/patch.go new file mode 100644 index 00000000..468ae670 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/patch.go @@ -0,0 +1,365 @@ +package gomonkey + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" + + "github.com/agiledragon/gomonkey/v2/creflect" +) + +type Patches struct { + originals map[uintptr][]byte + values map[reflect.Value]reflect.Value + valueHolders map[reflect.Value]reflect.Value +} + +type Params []interface{} +type OutputCell struct { + Values Params + Times int +} + +func ApplyFunc(target, double interface{}) *Patches { + return create().ApplyFunc(target, double) +} + +func ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { + return create().ApplyMethod(target, methodName, double) +} + +func ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { + return create().ApplyMethodFunc(target, methodName, doubleFunc) +} + +func ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { + return create().ApplyPrivateMethod(target, methodName, double) +} + +func ApplyGlobalVar(target, double interface{}) *Patches { + return create().ApplyGlobalVar(target, double) +} + +func ApplyFuncVar(target, double interface{}) *Patches { + return create().ApplyFuncVar(target, double) +} + +func ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { + return create().ApplyFuncSeq(target, outputs) +} + +func ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { + return create().ApplyMethodSeq(target, methodName, outputs) +} + +func ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { + return create().ApplyFuncVarSeq(target, outputs) +} + +func ApplyFuncReturn(target interface{}, output ...interface{}) *Patches { + return create().ApplyFuncReturn(target, output...) +} + +func ApplyMethodReturn(target interface{}, methodName string, output ...interface{}) *Patches { + return create().ApplyMethodReturn(target, methodName, output...) +} + +func ApplyFuncVarReturn(target interface{}, output ...interface{}) *Patches { + return create().ApplyFuncVarReturn(target, output...) +} + +func create() *Patches { + return &Patches{originals: make(map[uintptr][]byte), values: make(map[reflect.Value]reflect.Value), valueHolders: make(map[reflect.Value]reflect.Value)} +} + +func NewPatches() *Patches { + return create() +} + +func (this *Patches) ApplyFunc(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + d := reflect.ValueOf(double) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := reflect.ValueOf(double) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := funcToMethod(m.Type, doubleFunc) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { + m, ok := creflect.MethodByName(castRType(target), methodName) + if !ok { + panic("retrieve method by name failed") + } + d := reflect.ValueOf(double) + return this.ApplyCoreOnlyForPrivateMethod(m, d) +} + +func (this *Patches) ApplyGlobalVar(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + + this.values[t] = reflect.ValueOf(t.Elem().Interface()) + d := reflect.ValueOf(double) + t.Elem().Set(d) + return this +} + +func (this *Patches) ApplyFuncVar(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + d := reflect.ValueOf(double) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + this.check(t.Elem(), d) + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { + funcType := reflect.TypeOf(target) + t := reflect.ValueOf(target) + d := getDoubleFunc(funcType, outputs) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := getDoubleFunc(m.Type, outputs) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + if t.Elem().Kind() != reflect.Func { + panic("target is not a func") + } + + funcType := reflect.TypeOf(target).Elem() + double := getDoubleFunc(funcType, outputs).Interface() + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) ApplyFuncReturn(target interface{}, returns ...interface{}) *Patches { + funcType := reflect.TypeOf(target) + t := reflect.ValueOf(target) + outputs := []OutputCell{{Values: returns, Times: -1}} + d := getDoubleFunc(funcType, outputs) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethodReturn(target interface{}, methodName string, returns ...interface{}) *Patches { + m, ok := reflect.TypeOf(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + + outputs := []OutputCell{{Values: returns, Times: -1}} + d := getDoubleFunc(m.Type, outputs) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyFuncVarReturn(target interface{}, returns ...interface{}) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + if t.Elem().Kind() != reflect.Func { + panic("target is not a func") + } + + funcType := reflect.TypeOf(target).Elem() + outputs := []OutputCell{{Values: returns, Times: -1}} + double := getDoubleFunc(funcType, outputs).Interface() + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) Reset() { + for target, bytes := range this.originals { + modifyBinary(target, bytes) + delete(this.originals, target) + } + + for target, variable := range this.values { + target.Elem().Set(variable) + } +} + +func (this *Patches) ApplyCore(target, double reflect.Value) *Patches { + this.check(target, double) + assTarget := *(*uintptr)(getPointer(target)) + original := replace(assTarget, uintptr(getPointer(double))) + if _, ok := this.originals[assTarget]; !ok { + this.originals[assTarget] = original + } + this.valueHolders[double] = double + return this +} + +func (this *Patches) ApplyCoreOnlyForPrivateMethod(target unsafe.Pointer, double reflect.Value) *Patches { + if double.Kind() != reflect.Func { + panic("double is not a func") + } + assTarget := *(*uintptr)(target) + original := replace(assTarget, uintptr(getPointer(double))) + if _, ok := this.originals[assTarget]; !ok { + this.originals[assTarget] = original + } + this.valueHolders[double] = double + return this +} + +func (this *Patches) check(target, double reflect.Value) { + if target.Kind() != reflect.Func { + panic("target is not a func") + } + + if double.Kind() != reflect.Func { + panic("double is not a func") + } + + targetType := target.Type() + doubleType := double.Type() + + if targetType.NumIn() < doubleType.NumIn() || + targetType.NumOut() != doubleType.NumOut() || + (targetType.NumIn() == doubleType.NumIn() && targetType.IsVariadic() != doubleType.IsVariadic()) { + panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) + } + + for i, size := 0, doubleType.NumIn(); i < size; i++ { + targetIn := targetType.In(i) + doubleIn := doubleType.In(i) + + if targetIn.AssignableTo(doubleIn) { + continue + } + + panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) + } +} + +func replace(target, double uintptr) []byte { + code := buildJmpDirective(double) + bytes := entryAddress(target, len(code)) + original := make([]byte, len(bytes)) + copy(original, bytes) + modifyBinary(target, code) + return original +} + +func getDoubleFunc(funcType reflect.Type, outputs []OutputCell) reflect.Value { + if funcType.NumOut() != len(outputs[0].Values) { + panic(fmt.Sprintf("func type has %v return values, but only %v values provided as double", + funcType.NumOut(), len(outputs[0].Values))) + } + + needReturn := false + slice := make([]Params, 0) + for _, output := range outputs { + if output.Times == -1 { + needReturn = true + slice = []Params{output.Values} + break + } + t := 0 + if output.Times <= 1 { + t = 1 + } else { + t = output.Times + } + for j := 0; j < t; j++ { + slice = append(slice, output.Values) + } + } + + i := 0 + lenOutputs := len(slice) + return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value { + if needReturn { + return GetResultValues(funcType, slice[0]...) + } + if i < lenOutputs { + i++ + return GetResultValues(funcType, slice[i-1]...) + } + panic("double seq is less than call seq") + }) +} + +func GetResultValues(funcType reflect.Type, results ...interface{}) []reflect.Value { + var resultValues []reflect.Value + for i, r := range results { + var resultValue reflect.Value + if r == nil { + resultValue = reflect.Zero(funcType.Out(i)) + } else { + v := reflect.New(funcType.Out(i)) + v.Elem().Set(reflect.ValueOf(r)) + resultValue = v.Elem() + } + resultValues = append(resultValues, resultValue) + } + return resultValues +} + +type funcValue struct { + _ uintptr + p unsafe.Pointer +} + +func getPointer(v reflect.Value) unsafe.Pointer { + return (*funcValue)(unsafe.Pointer(&v)).p +} + +func entryAddress(p uintptr, l int) []byte { + return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: p, Len: l, Cap: l})) +} + +func pageStart(ptr uintptr) uintptr { + return ptr & ^(uintptr(syscall.Getpagesize() - 1)) +} + +func funcToMethod(funcType reflect.Type, doubleFunc interface{}) reflect.Value { + rf := reflect.TypeOf(doubleFunc) + if rf.Kind() != reflect.Func { + panic("doubleFunc is not a func") + } + vf := reflect.ValueOf(doubleFunc) + return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value { + if funcType.IsVariadic() { + return vf.CallSlice(in[1:]) + } else { + return vf.Call(in[1:]) + } + }) +} + +func castRType(val interface{}) reflect.Type { + if rTypeVal, ok := val.(reflect.Type); ok { + return rTypeVal + } + return reflect.TypeOf(val) +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s new file mode 100644 index 00000000..9b20a1f1 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s @@ -0,0 +1,64 @@ +/* + * Copyright 2022 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "textflag.h" + +#define NOP8 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; +#define NOP64 NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; +#define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; +#define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; + +#define protRW $(0x1|0x2|0x10) +#define mProtect $(0x2000000+74) + +TEXT ·write(SB),NOSPLIT,$24 + JMP START + NOP4096 +START: + MOVQ mProtect, AX + MOVQ page+24(FP), DI + MOVQ pageSize+32(FP), SI + MOVQ protRW, DX + SYSCALL + CMPQ AX, $0 + JZ PROTECT_OK + CALL mach_task_self(SB) + MOVQ AX, DI + MOVQ target+0(FP), SI + MOVQ len+16(FP), DX + MOVQ $0, CX + MOVQ protRW, R8 + CALL mach_vm_protect(SB) + CMPQ AX, $0 + JNZ RETURN +PROTECT_OK: + MOVQ target+0(FP), DI + MOVQ data+8(FP), SI + MOVQ len+16(FP), CX + MOVQ DI, to-24(SP) + MOVQ SI, from-16(SP) + MOVQ CX, n-8(SP) + CALL runtime·memmove(SB) + MOVQ mProtect, AX + MOVQ page+24(FP), DI + MOVQ pageSize+32(FP), SI + MOVQ oriProt+40(FP), DX + SYSCALL + JMP RETURN + NOP4096 +RETURN: + MOVQ AX, ret+48(FP) + RET diff --git a/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s new file mode 100644 index 00000000..c6cd3764 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s @@ -0,0 +1,63 @@ +/* + * Copyright 2022 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "textflag.h" + +#define NOP64 WORD $0x1f2003d5; WORD $0x1f2003d5; +#define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; +#define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; +#define NOP16384 NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; + +#define protRW $(0x1|0x2|0x10) +#define mProtect $(0x2000000+74) + +TEXT ·write(SB),NOSPLIT,$24 + B START + NOP16384 +START: + MOVD mProtect, R16 + MOVD page+24(FP), R0 + MOVD pageSize+32(FP), R1 + MOVD protRW, R2 + SVC $0x80 + CMP $0, R0 + BEQ PROTECT_OK + CALL mach_task_self(SB) + MOVD target+0(FP), R1 + MOVD len+16(FP), R2 + MOVD $0, R3 + MOVD protRW, R4 + CALL mach_vm_protect(SB) + CMP $0, R0 + BNE RETURN +PROTECT_OK: + MOVD target+0(FP), R0 + MOVD data+8(FP), R1 + MOVD len+16(FP), R2 + MOVD R0, to-24(SP) + MOVD R1, from-16(SP) + MOVD R2, n-8(SP) + CALL runtime·memmove(SB) + MOVD mProtect, R16 + MOVD page+24(FP), R0 + MOVD pageSize+32(FP), R1 + MOVD oriProt+40(FP), R2 + SVC $0x80 + B RETURN + NOP16384 +RETURN: + MOVD R0, ret+48(FP) + RET diff --git a/vendor/github.com/golang/mock/AUTHORS b/vendor/github.com/golang/mock/AUTHORS new file mode 100644 index 00000000..660b8ccc --- /dev/null +++ b/vendor/github.com/golang/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/github.com/golang/mock/CONTRIBUTORS b/vendor/github.com/golang/mock/CONTRIBUTORS new file mode 100644 index 00000000..def849ca --- /dev/null +++ b/vendor/github.com/golang/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/github.com/golang/mock/LICENSE b/vendor/github.com/golang/mock/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/github.com/golang/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/golang/mock/gomock/call.go b/vendor/github.com/golang/mock/gomock/call.go new file mode 100644 index 00000000..13c9f44b --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/call.go @@ -0,0 +1,445 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestHelper // for triggering test failures on invalid call setup + + receiver interface{} // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]interface{}) []interface{} +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + t.Helper() + + // TODO: check arity, types. + mArgs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + mArgs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + mArgs[i] = Nil() + } else { + mArgs[i] = Eq(arg) + } + } + + // callerInfo's skip should be updated if the number of calls between the user's test + // and this line changes, i.e. this code is wrapped in another anonymous function. + // 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test. + origin := callerInfo(3) + actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} { + // Synthesize the zero value for each of the return args' types. + rets := make([]interface{}, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{t: t, receiver: receiver, method: method, methodType: methodType, + args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions} +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes +// was previously called with 1, MinTimes also sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was +// previously called with 1, MaxTimes also sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) DoAndReturn(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + c.t.Helper() + vArgs := make([]reflect.Value, len(args)) + ft := v.Type() + if c.methodType.NumIn() != ft.NumIn() { + c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin) + return nil + } + for i := 0; i < len(args); i++ { + if args[i] != nil { + vArgs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vArgs[i] = reflect.Zero(ft.In(i)) + } + } + vRets := v.Call(vArgs) + rets := make([]interface{}, len(vRets)) + for i, ret := range vRets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) Do(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + c.t.Helper() + if c.methodType.NumIn() != v.Type().NumIn() { + c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, v.Type().NumIn(), c.methodType.NumIn(), c.origin) + return nil + } + vArgs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vArgs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vArgs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vArgs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...interface{}) *Call { + c.t.Helper() + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]interface{}) []interface{} { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice, SetArg +// will copy value's elements into the nth argument. +func (c *Call) SetArg(n int, value interface{}) *Call { + c.t.Helper() + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface: + // nothing to do + case reflect.Slice: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []interface{}) []interface{} { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + c.t.Helper() + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true if the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf( + "expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v", + c.origin, i, formatGottenArg(m, args[i]), m, + ) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vArgsType := c.methodType.In(c.methodType.NumIn() - 1) + vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i) + for _, arg := range args[i:] { + vArgs = reflect.Append(vArgs, reflect.ValueOf(arg)) + } + if m.Matches(vArgs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + + return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i]) + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call() []func([]interface{}) []interface{} { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +func setSlice(arg interface{}, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func (c *Call) addAction(action func([]interface{}) []interface{}) { + c.actions = append(c.actions, action) +} + +func formatGottenArg(m Matcher, arg interface{}) string { + got := fmt.Sprintf("%v (%T)", arg, arg) + if gs, ok := m.(GotFormatter); ok { + got = gs.Got(arg) + } + return got +} diff --git a/vendor/github.com/golang/mock/gomock/callset.go b/vendor/github.com/golang/mock/gomock/callset.go new file mode 100644 index 00000000..49dba787 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/callset.go @@ -0,0 +1,113 @@ +// Copyright 2011 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "bytes" + "errors" + "fmt" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver interface{} + fname string +} + +func newCallSet() *callSet { + return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)} +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { + key := callSetKey{receiver, method} + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + _, _ = fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + _, _ = fmt.Fprintf(&callsErrors, "\n%v", err) + continue + } + _, _ = fmt.Fprintf( + &callsErrors, "all expected calls for method %q have been exhausted", method, + ) + } + + if len(expected)+len(exhausted) == 0 { + _, _ = fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, errors.New(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} diff --git a/vendor/github.com/golang/mock/gomock/controller.go b/vendor/github.com/golang/mock/gomock/controller.go new file mode 100644 index 00000000..f054200d --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/controller.go @@ -0,0 +1,336 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package gomock is a mock framework for Go. +// +// Standard usage: +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +package gomock + +import ( + "context" + "fmt" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. It +// is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +// TestHelper is a TestReporter that has the Helper method. It is satisfied +// by the standard library's *testing.T. +type TestHelper interface { + TestReporter + Helper() +} + +// cleanuper is used to check if TestHelper also has the `Cleanup` method. A +// common pattern is to pass in a `*testing.T` to +// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup +// method. This can be utilized to call `Finish()` so the caller of this library +// does not have to. +type cleanuper interface { + Cleanup(func()) +} + +// A Controller represents the top-level control of a mock ecosystem. It +// defines the scope and lifetime of mock objects, as well as their +// expectations. It is safe to call Controller's methods from multiple +// goroutines. Each test should create a new Controller and invoke Finish via +// defer. +// +// func TestFoo(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// // .. +// } +// +// func TestBar(t *testing.T) { +// t.Run("Sub-Test-1", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// t.Run("Sub-Test-2", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// }) +type Controller struct { + // T should only be called within a generated mock. It is not intended to + // be used in user code and may be changed in future versions. T is the + // TestReporter passed in when creating the Controller via NewController. + // If the TestReporter does not implement a TestHelper it will be wrapped + // with a nopTestHelper. + T TestHelper + mu sync.Mutex + expectedCalls *callSet + finished bool +} + +// NewController returns a new Controller. It is the preferred way to create a +// Controller. +// +// New in go1.14+, if you are passing a *testing.T into this function you no +// longer need to call ctrl.Finish() in your test methods. +func NewController(t TestReporter) *Controller { + h, ok := t.(TestHelper) + if !ok { + h = &nopTestHelper{t} + } + ctrl := &Controller{ + T: h, + expectedCalls: newCallSet(), + } + if c, ok := isCleanuper(ctrl.T); ok { + c.Cleanup(func() { + ctrl.T.Helper() + ctrl.finish(true, nil) + }) + } + + return ctrl +} + +type cancelReporter struct { + t TestHelper + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...interface{}) { + r.t.Errorf(format, args...) +} +func (r *cancelReporter) Fatalf(format string, args ...interface{}) { + defer r.cancel() + r.t.Fatalf(format, args...) +} + +func (r *cancelReporter) Helper() { + r.t.Helper() +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + h, ok := t.(TestHelper) + if !ok { + h = &nopTestHelper{t: t} + } + + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{t: h, cancel: cancel}), ctx +} + +type nopTestHelper struct { + t TestReporter +} + +func (h *nopTestHelper) Errorf(format string, args ...interface{}) { + h.t.Errorf(format, args...) +} +func (h *nopTestHelper) Fatalf(format string, args ...interface{}) { + h.t.Fatalf(format, args...) +} + +func (h nopTestHelper) Helper() {} + +// RecordCall is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call { + ctrl.T.Helper() + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +// RecordCallWithMethodType is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + ctrl.T.Helper() + + call := newCall(ctrl.T, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +// Call is called by a mock. It should not be called by user code. +func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} { + ctrl.T.Helper() + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]interface{}) []interface{} { + ctrl.T.Helper() + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + // callerInfo's skip should be updated if the number of calls between the user's test + // and this line changes, i.e. this code is wrapped in another anonymous function. + // 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test. + origin := callerInfo(3) + ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequite calls, + // * and the prerequite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call() + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []interface{} + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +// Finish checks to see if all the methods that were expected to be called +// were called. It should be invoked for each Controller. It is not idempotent +// and therefore can only be invoked once. +// +// New in go1.14+, if you are passing a *testing.T into NewController function you no +// longer need to call ctrl.Finish() in your test methods. +func (ctrl *Controller) Finish() { + // If we're currently panicking, probably because this is a deferred call. + // This must be recovered in the deferred function. + err := recover() + ctrl.finish(false, err) +} + +func (ctrl *Controller) finish(cleanup bool, panicErr interface{}) { + ctrl.T.Helper() + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + if _, ok := isCleanuper(ctrl.T); !ok { + ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + return + } + ctrl.finished = true + + // Short-circuit, pass through the panic. + if panicErr != nil { + panic(panicErr) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.T.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + if !cleanup { + ctrl.T.Fatalf("aborting test due to missing call(s)") + return + } + ctrl.T.Errorf("aborting test due to missing call(s)") + } +} + +// callerInfo returns the file:line of the call site. skip is the number +// of stack frames to skip when reporting. 0 is callerInfo's call site. +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} + +// isCleanuper checks it if t's base TestReporter has a Cleanup method. +func isCleanuper(t TestReporter) (cleanuper, bool) { + tr := unwrapTestReporter(t) + c, ok := tr.(cleanuper) + return c, ok +} + +// unwrapTestReporter unwraps TestReporter to the base implementation. +func unwrapTestReporter(t TestReporter) TestReporter { + tr := t + switch nt := t.(type) { + case *cancelReporter: + tr = nt.t + if h, check := tr.(*nopTestHelper); check { + tr = h.t + } + case *nopTestHelper: + tr = nt.t + default: + // not wrapped + } + return tr +} diff --git a/vendor/github.com/golang/mock/gomock/matchers.go b/vendor/github.com/golang/mock/gomock/matchers.go new file mode 100644 index 00000000..2822fb2c --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/matchers.go @@ -0,0 +1,341 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" + "strings" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x interface{}) bool + + // String describes what the matcher matches. + String() string +} + +// WantFormatter modifies the given Matcher's String() method to the given +// Stringer. This allows for control on how the "Want" is formatted when +// printing . +func WantFormatter(s fmt.Stringer, m Matcher) Matcher { + type matcher interface { + Matches(x interface{}) bool + } + + return struct { + matcher + fmt.Stringer + }{ + matcher: m, + Stringer: s, + } +} + +// StringerFunc type is an adapter to allow the use of ordinary functions as +// a Stringer. If f is a function with the appropriate signature, +// StringerFunc(f) is a Stringer that calls f. +type StringerFunc func() string + +// String implements fmt.Stringer. +func (f StringerFunc) String() string { + return f() +} + +// GotFormatter is used to better print failure messages. If a matcher +// implements GotFormatter, it will use the result from Got when printing +// the failure message. +type GotFormatter interface { + // Got is invoked with the received value. The result is used when + // printing the failure message. + Got(got interface{}) string +} + +// GotFormatterFunc type is an adapter to allow the use of ordinary +// functions as a GotFormatter. If f is a function with the appropriate +// signature, GotFormatterFunc(f) is a GotFormatter that calls f. +type GotFormatterFunc func(got interface{}) string + +// Got implements GotFormatter. +func (f GotFormatterFunc) Got(got interface{}) string { + return f(got) +} + +// GotFormatterAdapter attaches a GotFormatter to a Matcher. +func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher { + return struct { + GotFormatter + Matcher + }{ + GotFormatter: s, + Matcher: m, + } +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(interface{}) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type eqMatcher struct { + x interface{} +} + +func (e eqMatcher) Matches(x interface{}) bool { + // In case, some value is nil + if e.x == nil || x == nil { + return reflect.DeepEqual(e.x, x) + } + + // Check if types assignable and convert them to common type + x1Val := reflect.ValueOf(e.x) + x2Val := reflect.ValueOf(x) + + if x1Val.Type().AssignableTo(x2Val.Type()) { + x1ValConverted := x1Val.Convert(x2Val.Type()) + return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface()) + } + + return false +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %v (%T)", e.x, e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x interface{}) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x interface{}) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + return "not(" + n.m.String() + ")" +} + +type assignableToTypeOfMatcher struct { + targetType reflect.Type +} + +func (m assignableToTypeOfMatcher) Matches(x interface{}) bool { + return reflect.TypeOf(x).AssignableTo(m.targetType) +} + +func (m assignableToTypeOfMatcher) String() string { + return "is assignable to " + m.targetType.Name() +} + +type allMatcher struct { + matchers []Matcher +} + +func (am allMatcher) Matches(x interface{}) bool { + for _, m := range am.matchers { + if !m.Matches(x) { + return false + } + } + return true +} + +func (am allMatcher) String() string { + ss := make([]string, 0, len(am.matchers)) + for _, matcher := range am.matchers { + ss = append(ss, matcher.String()) + } + return strings.Join(ss, "; ") +} + +type lenMatcher struct { + i int +} + +func (m lenMatcher) Matches(x interface{}) bool { + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == m.i + default: + return false + } +} + +func (m lenMatcher) String() string { + return fmt.Sprintf("has length %d", m.i) +} + +type inAnyOrderMatcher struct { + x interface{} +} + +func (m inAnyOrderMatcher) Matches(x interface{}) bool { + given, ok := m.prepareValue(x) + if !ok { + return false + } + wanted, ok := m.prepareValue(m.x) + if !ok { + return false + } + + if given.Len() != wanted.Len() { + return false + } + + usedFromGiven := make([]bool, given.Len()) + foundFromWanted := make([]bool, wanted.Len()) + for i := 0; i < wanted.Len(); i++ { + wantedMatcher := Eq(wanted.Index(i).Interface()) + for j := 0; j < given.Len(); j++ { + if usedFromGiven[j] { + continue + } + if wantedMatcher.Matches(given.Index(j).Interface()) { + foundFromWanted[i] = true + usedFromGiven[j] = true + break + } + } + } + + missingFromWanted := 0 + for _, found := range foundFromWanted { + if !found { + missingFromWanted++ + } + } + extraInGiven := 0 + for _, used := range usedFromGiven { + if !used { + extraInGiven++ + } + } + + return extraInGiven == 0 && missingFromWanted == 0 +} + +func (m inAnyOrderMatcher) prepareValue(x interface{}) (reflect.Value, bool) { + xValue := reflect.ValueOf(x) + switch xValue.Kind() { + case reflect.Slice, reflect.Array: + return xValue, true + default: + return reflect.Value{}, false + } +} + +func (m inAnyOrderMatcher) String() string { + return fmt.Sprintf("has the same elements as %v", m.x) +} + +// Constructors + +// All returns a composite Matcher that returns true if and only all of the +// matchers return true. +func All(ms ...Matcher) Matcher { return allMatcher{ms} } + +// Any returns a matcher that always matches. +func Any() Matcher { return anyMatcher{} } + +// Eq returns a matcher that matches on equality. +// +// Example usage: +// Eq(5).Matches(5) // returns true +// Eq(5).Matches(4) // returns false +func Eq(x interface{}) Matcher { return eqMatcher{x} } + +// Len returns a matcher that matches on length. This matcher returns false if +// is compared to a type that is not an array, chan, map, slice, or string. +func Len(i int) Matcher { + return lenMatcher{i} +} + +// Nil returns a matcher that matches if the received value is nil. +// +// Example usage: +// var x *bytes.Buffer +// Nil().Matches(x) // returns true +// x = &bytes.Buffer{} +// Nil().Matches(x) // returns false +func Nil() Matcher { return nilMatcher{} } + +// Not reverses the results of its given child matcher. +// +// Example usage: +// Not(Eq(5)).Matches(4) // returns true +// Not(Eq(5)).Matches(5) // returns false +func Not(x interface{}) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} + +// AssignableToTypeOf is a Matcher that matches if the parameter to the mock +// function is assignable to the type of the parameter to this function. +// +// Example usage: +// var s fmt.Stringer = &bytes.Buffer{} +// AssignableToTypeOf(s).Matches(time.Second) // returns true +// AssignableToTypeOf(s).Matches(99) // returns false +// +// var ctx = reflect.TypeOf((*context.Context)(nil)).Elem() +// AssignableToTypeOf(ctx).Matches(context.Background()) // returns true +func AssignableToTypeOf(x interface{}) Matcher { + if xt, ok := x.(reflect.Type); ok { + return assignableToTypeOfMatcher{xt} + } + return assignableToTypeOfMatcher{reflect.TypeOf(x)} +} + +// InAnyOrder is a Matcher that returns true for collections of the same elements ignoring the order. +// +// Example usage: +// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 3, 2}) // returns true +// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 2}) // returns false +func InAnyOrder(x interface{}) Matcher { + return inAnyOrderMatcher{x} +} diff --git a/vendor/golang.org/x/crypto/internal/alias/alias.go b/vendor/golang.org/x/crypto/internal/alias/alias.go index 69c17f82..551ff0c3 100644 --- a/vendor/golang.org/x/crypto/internal/alias/alias.go +++ b/vendor/golang.org/x/crypto/internal/alias/alias.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !purego -// +build !purego // Package alias implements memory aliasing tests. package alias diff --git a/vendor/golang.org/x/crypto/internal/alias/alias_purego.go b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go index 4775b0a4..6fe61b5c 100644 --- a/vendor/golang.org/x/crypto/internal/alias/alias_purego.go +++ b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build purego -// +build purego // Package alias implements memory aliasing tests. package alias diff --git a/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go b/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go index 45b5c966..d33c8890 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.13 -// +build !go1.13 package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go b/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go index ed52b341..495c1fa6 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.13 -// +build go1.13 package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index f184b67d..333da285 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!amd64 && !ppc64le && !s390x) || !gc || purego -// +build !amd64,!ppc64le,!s390x !gc purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go index 6d522333..164cd47d 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s index 1d74f0f8..e0d3c647 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go index 4a069941..4aec4874 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s index 58422aad..d2ca5dee 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go index ec959668..e1d033a4 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s index aa9e0494..0fe3a7c2 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go index c400dfcf..e76b44fe 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc package salsa diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s index c0892772..fcce0234 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go index 4392cc1a..9448760f 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || purego || !gc -// +build !amd64 purego !gc package salsa diff --git a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go index 2681af35..150f887e 100644 --- a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go +++ b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go @@ -13,7 +13,7 @@ import ( "sync" ) -// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be +// Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be // compiled the first time it is needed. type Regexp struct { str string diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go index e9dec6e6..2a364b22 100644 --- a/vendor/golang.org/x/mod/module/module.go +++ b/vendor/golang.org/x/mod/module/module.go @@ -4,7 +4,7 @@ // Package module defines the module.Version type along with support code. // -// The module.Version type is a simple Path, Version pair: +// The [module.Version] type is a simple Path, Version pair: // // type Version struct { // Path string @@ -12,7 +12,7 @@ // } // // There are no restrictions imposed directly by use of this structure, -// but additional checking functions, most notably Check, verify that +// but additional checking functions, most notably [Check], verify that // a particular path, version pair is valid. // // # Escaped Paths @@ -140,7 +140,7 @@ type ModuleError struct { Err error } -// VersionError returns a ModuleError derived from a Version and error, +// VersionError returns a [ModuleError] derived from a [Version] and error, // or err itself if it is already such an error. func VersionError(v Version, err error) error { var mErr *ModuleError @@ -169,7 +169,7 @@ func (e *ModuleError) Unwrap() error { return e.Err } // An InvalidVersionError indicates an error specific to a version, with the // module path unknown or specified externally. // -// A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError +// A [ModuleError] may wrap an InvalidVersionError, but an InvalidVersionError // must not wrap a ModuleError. type InvalidVersionError struct { Version string @@ -193,8 +193,8 @@ func (e *InvalidVersionError) Error() string { func (e *InvalidVersionError) Unwrap() error { return e.Err } // An InvalidPathError indicates a module, import, or file path doesn't -// satisfy all naming constraints. See CheckPath, CheckImportPath, -// and CheckFilePath for specific restrictions. +// satisfy all naming constraints. See [CheckPath], [CheckImportPath], +// and [CheckFilePath] for specific restrictions. type InvalidPathError struct { Kind string // "module", "import", or "file" Path string @@ -294,7 +294,7 @@ func fileNameOK(r rune) bool { } // CheckPath checks that a module path is valid. -// A valid module path is a valid import path, as checked by CheckImportPath, +// A valid module path is a valid import path, as checked by [CheckImportPath], // with three additional constraints. // First, the leading path element (up to the first slash, if any), // by convention a domain name, must contain only lower-case ASCII letters, @@ -380,7 +380,7 @@ const ( // checkPath returns an error describing why the path is not valid. // Because these checks apply to module, import, and file paths, // and because other checks may be applied, the caller is expected to wrap -// this error with InvalidPathError. +// this error with [InvalidPathError]. func checkPath(path string, kind pathKind) error { if !utf8.ValidString(path) { return fmt.Errorf("invalid UTF-8") @@ -532,7 +532,7 @@ var badWindowsNames = []string{ // they require ".vN" instead of "/vN", and for all N, not just N >= 2. // SplitPathVersion returns with ok = false when presented with // a path whose last path element does not satisfy the constraints -// applied by CheckPath, such as "example.com/pkg/v1" or "example.com/pkg/v1.2". +// applied by [CheckPath], such as "example.com/pkg/v1" or "example.com/pkg/v1.2". func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { if strings.HasPrefix(path, "gopkg.in/") { return splitGopkgIn(path) @@ -582,7 +582,7 @@ func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { // MatchPathMajor reports whether the semantic version v // matches the path major version pathMajor. // -// MatchPathMajor returns true if and only if CheckPathMajor returns nil. +// MatchPathMajor returns true if and only if [CheckPathMajor] returns nil. func MatchPathMajor(v, pathMajor string) bool { return CheckPathMajor(v, pathMajor) == nil } @@ -622,7 +622,7 @@ func CheckPathMajor(v, pathMajor string) error { // PathMajorPrefix returns the major-version tag prefix implied by pathMajor. // An empty PathMajorPrefix allows either v0 or v1. // -// Note that MatchPathMajor may accept some versions that do not actually begin +// Note that [MatchPathMajor] may accept some versions that do not actually begin // with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1' // pathMajor, even though that pathMajor implies 'v1' tagging. func PathMajorPrefix(pathMajor string) string { @@ -643,7 +643,7 @@ func PathMajorPrefix(pathMajor string) string { } // CanonicalVersion returns the canonical form of the version string v. -// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +// It is the same as [semver.Canonical] except that it preserves the special build suffix "+incompatible". func CanonicalVersion(v string) string { cv := semver.Canonical(v) if semver.Build(v) == "+incompatible" { @@ -652,8 +652,8 @@ func CanonicalVersion(v string) string { return cv } -// Sort sorts the list by Path, breaking ties by comparing Version fields. -// The Version fields are interpreted as semantic versions (using semver.Compare) +// Sort sorts the list by Path, breaking ties by comparing [Version] fields. +// The Version fields are interpreted as semantic versions (using [semver.Compare]) // optionally followed by a tie-breaking suffix introduced by a slash character, // like in "v0.0.1/go.mod". func Sort(list []Version) { @@ -793,7 +793,7 @@ func unescapeString(escaped string) (string, bool) { } // MatchPrefixPatterns reports whether any path prefix of target matches one of -// the glob patterns (as defined by path.Match) in the comma-separated globs +// the glob patterns (as defined by [path.Match]) in the comma-separated globs // list. This implements the algorithm used when matching a module path to the // GOPRIVATE environment variable, as described by 'go help module-private'. // diff --git a/vendor/golang.org/x/mod/module/pseudo.go b/vendor/golang.org/x/mod/module/pseudo.go index f04ad378..9cf19d32 100644 --- a/vendor/golang.org/x/mod/module/pseudo.go +++ b/vendor/golang.org/x/mod/module/pseudo.go @@ -125,7 +125,7 @@ func IsPseudoVersion(v string) bool { } // IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base, -// timestamp, and revision, as returned by ZeroPseudoVersion. +// timestamp, and revision, as returned by [ZeroPseudoVersion]. func IsZeroPseudoVersion(v string) bool { return v == ZeroPseudoVersion(semver.Major(v)) } diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go index a30a22bf..9a2dfd33 100644 --- a/vendor/golang.org/x/mod/semver/semver.go +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -140,7 +140,7 @@ func Compare(v, w string) int { // Max canonicalizes its arguments and then returns the version string // that compares greater. // -// Deprecated: use Compare instead. In most cases, returning a canonicalized +// Deprecated: use [Compare] instead. In most cases, returning a canonicalized // version is not expected or desired. func Max(v, w string) string { v = Canonical(v) @@ -151,7 +151,7 @@ func Max(v, w string) string { return w } -// ByVersion implements sort.Interface for sorting semantic version strings. +// ByVersion implements [sort.Interface] for sorting semantic version strings. type ByVersion []string func (vs ByVersion) Len() int { return len(vs) } @@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool { return vs[i] < vs[j] } -// Sort sorts a list of semantic version strings using ByVersion. +// Sort sorts a list of semantic version strings using [ByVersion]. func Sort(list []string) { sort.Sort(ByVersion(list)) } diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index 2cb9c408..0c1b8679 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.7 -// +build go1.7 package context diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go index 64d31ecc..e31e35a9 100644 --- a/vendor/golang.org/x/net/context/go19.go +++ b/vendor/golang.org/x/net/context/go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.9 -// +build go1.9 package context diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 7b6b6851..065ff3df 100644 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.7 -// +build !go1.7 package context diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go index 1f971534..ec5a6380 100644 --- a/vendor/golang.org/x/net/context/pre_go19.go +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.9 -// +build !go1.9 package context diff --git a/vendor/golang.org/x/net/http2/databuffer.go b/vendor/golang.org/x/net/http2/databuffer.go index a3067f8d..e6f55cbd 100644 --- a/vendor/golang.org/x/net/http2/databuffer.go +++ b/vendor/golang.org/x/net/http2/databuffer.go @@ -20,41 +20,44 @@ import ( // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) -var ( - dataChunkSizeClasses = []int{ - 1 << 10, - 2 << 10, - 4 << 10, - 8 << 10, - 16 << 10, - } - dataChunkPools = [...]sync.Pool{ - {New: func() interface{} { return make([]byte, 1<<10) }}, - {New: func() interface{} { return make([]byte, 2<<10) }}, - {New: func() interface{} { return make([]byte, 4<<10) }}, - {New: func() interface{} { return make([]byte, 8<<10) }}, - {New: func() interface{} { return make([]byte, 16<<10) }}, - } -) +var dataChunkPools = [...]sync.Pool{ + {New: func() interface{} { return new([1 << 10]byte) }}, + {New: func() interface{} { return new([2 << 10]byte) }}, + {New: func() interface{} { return new([4 << 10]byte) }}, + {New: func() interface{} { return new([8 << 10]byte) }}, + {New: func() interface{} { return new([16 << 10]byte) }}, +} func getDataBufferChunk(size int64) []byte { - i := 0 - for ; i < len(dataChunkSizeClasses)-1; i++ { - if size <= int64(dataChunkSizeClasses[i]) { - break - } + switch { + case size <= 1<<10: + return dataChunkPools[0].Get().(*[1 << 10]byte)[:] + case size <= 2<<10: + return dataChunkPools[1].Get().(*[2 << 10]byte)[:] + case size <= 4<<10: + return dataChunkPools[2].Get().(*[4 << 10]byte)[:] + case size <= 8<<10: + return dataChunkPools[3].Get().(*[8 << 10]byte)[:] + default: + return dataChunkPools[4].Get().(*[16 << 10]byte)[:] } - return dataChunkPools[i].Get().([]byte) } func putDataBufferChunk(p []byte) { - for i, n := range dataChunkSizeClasses { - if len(p) == n { - dataChunkPools[i].Put(p) - return - } + switch len(p) { + case 1 << 10: + dataChunkPools[0].Put((*[1 << 10]byte)(p)) + case 2 << 10: + dataChunkPools[1].Put((*[2 << 10]byte)(p)) + case 4 << 10: + dataChunkPools[2].Put((*[4 << 10]byte)(p)) + case 8 << 10: + dataChunkPools[3].Put((*[8 << 10]byte)(p)) + case 16 << 10: + dataChunkPools[4].Put((*[16 << 10]byte)(p)) + default: + panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } - panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } // dataBuffer is an io.ReadWriter backed by a list of data chunks. diff --git a/vendor/golang.org/x/net/http2/go111.go b/vendor/golang.org/x/net/http2/go111.go deleted file mode 100644 index 5bf62b03..00000000 --- a/vendor/golang.org/x/net/http2/go111.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.11 -// +build go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - if trace != nil { - return trace.Got1xxResponse - } - return nil -} diff --git a/vendor/golang.org/x/net/http2/go115.go b/vendor/golang.org/x/net/http2/go115.go deleted file mode 100644 index 908af1ab..00000000 --- a/vendor/golang.org/x/net/http2/go115.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.15 -// +build go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS -// connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - dialer := &tls.Dialer{ - Config: cfg, - } - cn, err := dialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed - return tlsCn, nil -} diff --git a/vendor/golang.org/x/net/http2/go118.go b/vendor/golang.org/x/net/http2/go118.go deleted file mode 100644 index aca4b2b3..00000000 --- a/vendor/golang.org/x/net/http2/go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return tc.NetConn() -} diff --git a/vendor/golang.org/x/net/http2/not_go111.go b/vendor/golang.org/x/net/http2/not_go111.go deleted file mode 100644 index cc0baa81..00000000 --- a/vendor/golang.org/x/net/http2/not_go111.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.11 -// +build !go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false } - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - return nil -} diff --git a/vendor/golang.org/x/net/http2/not_go115.go b/vendor/golang.org/x/net/http2/not_go115.go deleted file mode 100644 index e6c04cf7..00000000 --- a/vendor/golang.org/x/net/http2/not_go115.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.15 -// +build !go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext opens a TLS connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - cn, err := tls.Dial(network, addr, cfg) - if err != nil { - return nil, err - } - if err := cn.Handshake(); err != nil { - return nil, err - } - if cfg.InsecureSkipVerify { - return cn, nil - } - if err := cn.VerifyHostname(cfg.ServerName); err != nil { - return nil, err - } - return cn, nil -} diff --git a/vendor/golang.org/x/net/http2/not_go118.go b/vendor/golang.org/x/net/http2/not_go118.go deleted file mode 100644 index eab532c9..00000000 --- a/vendor/golang.org/x/net/http2/not_go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return nil -} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 02c88b6b..ae94c640 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -2549,7 +2549,6 @@ type responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished - dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -2669,7 +2668,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { - rws.dirty = true return 0, err } if endStream { @@ -2690,7 +2688,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { - rws.dirty = true return 0, err } } @@ -2702,9 +2699,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) - if err != nil { - rws.dirty = true - } return len(p), err } return len(p), nil @@ -2920,14 +2914,12 @@ func (rws *responseWriterState) writeHeader(code int) { h.Del("Transfer-Encoding") } - if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ + rws.conn.writeHeaders(rws.stream, &writeResHeaders{ streamID: rws.stream.id, httpResCode: code, h: h, endStream: rws.handlerDone && !rws.hasTrailers(), - }) != nil { - rws.dirty = true - } + }) return } @@ -2992,19 +2984,10 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, func (w *responseWriter) handlerDone() { rws := w.rws - dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - if !dirty { - // Only recycle the pool if all prior Write calls to - // the serverConn goroutine completed successfully. If - // they returned earlier due to resets from the peer - // there might still be write goroutines outstanding - // from the serverConn referencing the rws memory. See - // issue 20704. - responseWriterStatePool.Put(rws) - } + responseWriterStatePool.Put(rws) } // Push errors. @@ -3187,6 +3170,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) { panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) } + sc.curHandlers++ go sc.runHandler(rw, req, sc.handler.ServeHTTP) return promisedID, nil } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 4515b22c..df578b86 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -1018,7 +1018,7 @@ func (cc *ClientConn) forceCloseConn() { if !ok { return } - if nc := tlsUnderlyingConn(tc); nc != nil { + if nc := tc.NetConn(); nc != nil { nc.Close() } } @@ -3201,3 +3201,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) { trace.GotFirstResponseByte() } } + +func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} diff --git a/vendor/golang.org/x/net/idna/go118.go b/vendor/golang.org/x/net/idna/go118.go index c5c4338d..712f1ad8 100644 --- a/vendor/golang.org/x/net/idna/go118.go +++ b/vendor/golang.org/x/net/idna/go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.18 -// +build go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/idna10.0.0.go b/vendor/golang.org/x/net/idna/idna10.0.0.go index 64ccf85f..7b371788 100644 --- a/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/idna9.0.0.go b/vendor/golang.org/x/net/idna/idna9.0.0.go index ee1698ce..cc6a892a 100644 --- a/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/pre_go118.go b/vendor/golang.org/x/net/idna/pre_go118.go index 3aaccab1..40e74bb3 100644 --- a/vendor/golang.org/x/net/idna/pre_go118.go +++ b/vendor/golang.org/x/net/idna/pre_go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.18 -// +build !go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/tables10.0.0.go b/vendor/golang.org/x/net/idna/tables10.0.0.go index d1d62ef4..c6c2bf10 100644 --- a/vendor/golang.org/x/net/idna/tables10.0.0.go +++ b/vendor/golang.org/x/net/idna/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package idna diff --git a/vendor/golang.org/x/net/idna/tables11.0.0.go b/vendor/golang.org/x/net/idna/tables11.0.0.go index 167efba7..76789393 100644 --- a/vendor/golang.org/x/net/idna/tables11.0.0.go +++ b/vendor/golang.org/x/net/idna/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package idna diff --git a/vendor/golang.org/x/net/idna/tables12.0.0.go b/vendor/golang.org/x/net/idna/tables12.0.0.go index ab40f7bc..0600cd2a 100644 --- a/vendor/golang.org/x/net/idna/tables12.0.0.go +++ b/vendor/golang.org/x/net/idna/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables13.0.0.go b/vendor/golang.org/x/net/idna/tables13.0.0.go index 66701ead..2fb768ef 100644 --- a/vendor/golang.org/x/net/idna/tables13.0.0.go +++ b/vendor/golang.org/x/net/idna/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables15.0.0.go b/vendor/golang.org/x/net/idna/tables15.0.0.go index 40033778..5ff05fe1 100644 --- a/vendor/golang.org/x/net/idna/tables15.0.0.go +++ b/vendor/golang.org/x/net/idna/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables9.0.0.go b/vendor/golang.org/x/net/idna/tables9.0.0.go index 4074b533..0f25e84c 100644 --- a/vendor/golang.org/x/net/idna/tables9.0.0.go +++ b/vendor/golang.org/x/net/idna/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package idna diff --git a/vendor/golang.org/x/net/idna/trie12.0.0.go b/vendor/golang.org/x/net/idna/trie12.0.0.go index bb63f904..8a75b966 100644 --- a/vendor/golang.org/x/net/idna/trie12.0.0.go +++ b/vendor/golang.org/x/net/idna/trie12.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.16 -// +build !go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/trie13.0.0.go b/vendor/golang.org/x/net/idna/trie13.0.0.go index 7d68a8dc..fa45bb90 100644 --- a/vendor/golang.org/x/net/idna/trie13.0.0.go +++ b/vendor/golang.org/x/net/idna/trie13.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.16 -// +build go1.16 package idna diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr.go b/vendor/golang.org/x/net/internal/socket/cmsghdr.go index 4bdaaaf1..33a5bf59 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go index 0d30e0a0..68f438c8 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go index 4936e8a6..058ea8de 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go index f6877f98..3ca0d3a0 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go index d3dbe1b8..6d0e426c 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go index 1d9f2ed6..7ca9cb7e 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go index 19d46789..0211f225 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go index 5b1d50ae..2038f290 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go index be634095..70e6f448 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || windows || zos -// +build aix windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/empty.s b/vendor/golang.org/x/net/internal/socket/empty.s index 90ab4ca3..49d79791 100644 --- a/vendor/golang.org/x/net/internal/socket/empty.s +++ b/vendor/golang.org/x/net/internal/socket/empty.s @@ -3,6 +3,5 @@ // license that can be found in the LICENSE file. //go:build darwin && go1.12 -// +build darwin,go1.12 // This exists solely so we can linkname in symbols from syscall. diff --git a/vendor/golang.org/x/net/internal/socket/error_unix.go b/vendor/golang.org/x/net/internal/socket/error_unix.go index 78f41290..7a5cc5c4 100644 --- a/vendor/golang.org/x/net/internal/socket/error_unix.go +++ b/vendor/golang.org/x/net/internal/socket/error_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go index 2b8fbb3f..340e53fb 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd) -// +build arm mips mipsle 386 ppc -// +build darwin dragonfly freebsd linux netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go index 2e94e96f..26470c19 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos) -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build aix darwin dragonfly freebsd linux netbsd openbsd zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go index f7da2bc4..8859ce10 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_stub.go b/vendor/golang.org/x/net/internal/socket/iovec_stub.go index 14caf524..da886b03 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_stub.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go index 113e773c..4825b21e 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !linux && !netbsd -// +build !aix,!linux,!netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go index 41883c53..311fd2c7 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || netbsd -// +build aix linux netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go index 25f6847f..ebff4f6e 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go index 5b8e00f1..62e6fe86 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd -// +build aix darwin dragonfly freebsd netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go index b4658fba..3dd07250 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go index 42411aff..5af9ddd6 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go index 3098f5d7..e212b50f 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go index eb79151f..e8767764 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go index 324e9ee7..529db68e 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build s390x && zos -// +build s390x,zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/norace.go b/vendor/golang.org/x/net/internal/socket/norace.go index de0ad420..8af30ecf 100644 --- a/vendor/golang.org/x/net/internal/socket/norace.go +++ b/vendor/golang.org/x/net/internal/socket/norace.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race package socket diff --git a/vendor/golang.org/x/net/internal/socket/race.go b/vendor/golang.org/x/net/internal/socket/race.go index f0a28a62..9afa9580 100644 --- a/vendor/golang.org/x/net/internal/socket/race.go +++ b/vendor/golang.org/x/net/internal/socket/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go index 8f79b38f..04313907 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go index f7d0b0d2..7c0d7410 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go index 02f32855..e363fb5a 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go index dd785877..ff7a8baf 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_bsd.go b/vendor/golang.org/x/net/internal/socket/sys_bsd.go index b258879d..e7664d48 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || openbsd || solaris -// +build aix darwin dragonfly freebsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go index 5d99f237..d7627f87 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux.go b/vendor/golang.org/x/net/internal/socket/sys_linux.go index 76f5b8ae..08d49107 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !s390x && !386 -// +build linux,!s390x,!386 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go index af964e61..1d182470 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go index 5b128fbb..0e407d12 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_posix.go b/vendor/golang.org/x/net/internal/socket/sys_posix.go index 42b8f234..58d86548 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_posix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_stub.go b/vendor/golang.org/x/net/internal/socket/sys_stub.go index 7cfb349c..2e5b473c 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_stub.go +++ b/vendor/golang.org/x/net/internal/socket/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_unix.go b/vendor/golang.org/x/net/internal/socket/sys_unix.go index de823932..93058db5 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go index 00691bd5..45bab004 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go index 6a94fec2..b6fc15a1 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go index c066272d..e67fc3cb 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/ipv4/control_bsd.go b/vendor/golang.org/x/net/ipv4/control_bsd.go index b7385dfd..c88da8cb 100644 --- a/vendor/golang.org/x/net/ipv4/control_bsd.go +++ b/vendor/golang.org/x/net/ipv4/control_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_pktinfo.go b/vendor/golang.org/x/net/ipv4/control_pktinfo.go index 0e748dbd..14ae2dae 100644 --- a/vendor/golang.org/x/net/ipv4/control_pktinfo.go +++ b/vendor/golang.org/x/net/ipv4/control_pktinfo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || linux || solaris -// +build darwin linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_stub.go b/vendor/golang.org/x/net/ipv4/control_stub.go index f27322c3..3ba66116 100644 --- a/vendor/golang.org/x/net/ipv4/control_stub.go +++ b/vendor/golang.org/x/net/ipv4/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_unix.go b/vendor/golang.org/x/net/ipv4/control_unix.go index 2413e02f..2e765548 100644 --- a/vendor/golang.org/x/net/ipv4/control_unix.go +++ b/vendor/golang.org/x/net/ipv4/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/icmp_stub.go b/vendor/golang.org/x/net/ipv4/icmp_stub.go index cd4ee6e1..c2c4ce7f 100644 --- a/vendor/golang.org/x/net/ipv4/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv4/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg.go b/vendor/golang.org/x/net/ipv4/payload_cmsg.go index 1bb370e2..91c685e8 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go index 53f0794e..2afd4b50 100644 --- a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_posix.go b/vendor/golang.org/x/net/ipv4/sockopt_posix.go index eb07c1c0..82e2c378 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_stub.go b/vendor/golang.org/x/net/ipv4/sockopt_stub.go index cf036893..840108bf 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_aix.go b/vendor/golang.org/x/net/ipv4/sys_aix.go index 02730cdf..9244a68a 100644 --- a/vendor/golang.org/x/net/ipv4/sys_aix.go +++ b/vendor/golang.org/x/net/ipv4/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq.go b/vendor/golang.org/x/net/ipv4/sys_asmreq.go index 22322b38..645f254c 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd netbsd openbsd solaris windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go index fde64014..48cfb6db 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go index 54eb9901..0b27b632 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux -// +build darwin freebsd linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go index dcb15f25..303a5e2e 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux -// +build !darwin,!freebsd,!linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf.go b/vendor/golang.org/x/net/ipv4/sys_bpf.go index fb11e324..1b4780df 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go index fc53a0d3..b1f779b4 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bsd.go b/vendor/golang.org/x/net/ipv4/sys_bsd.go index e191b2f1..b7b032d2 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv4/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build netbsd || openbsd -// +build netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go index 6a4e7abf..a295e15e 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux || solaris -// +build darwin freebsd linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go index 157159fd..74bd454e 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux && !solaris -// +build !darwin,!freebsd,!linux,!solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_stub.go b/vendor/golang.org/x/net/ipv4/sys_stub.go index d5508516..20af4074 100644 --- a/vendor/golang.org/x/net/ipv4/sys_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go index b7f2d6e5..dd454025 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go index e15c22c7..54f9e139 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go index e2edebdb..78374a52 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go index 2733ddbe..a8f04e7b 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go index 9c90844a..51fbbb1f 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_stub.go b/vendor/golang.org/x/net/ipv6/control_stub.go index b7e8643f..eb28ce75 100644 --- a/vendor/golang.org/x/net/ipv6/control_stub.go +++ b/vendor/golang.org/x/net/ipv6/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_unix.go b/vendor/golang.org/x/net/ipv6/control_unix.go index 63e475db..9c73b864 100644 --- a/vendor/golang.org/x/net/ipv6/control_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_bsd.go b/vendor/golang.org/x/net/ipv6/icmp_bsd.go index 120bf877..2814534a 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_bsd.go +++ b/vendor/golang.org/x/net/ipv6/icmp_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_stub.go b/vendor/golang.org/x/net/ipv6/icmp_stub.go index d60136a9..c92c9b51 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv6/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg.go b/vendor/golang.org/x/net/ipv6/payload_cmsg.go index b0692e43..be04e4d6 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go index cd0ff508..29b9ccf6 100644 --- a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_posix.go b/vendor/golang.org/x/net/ipv6/sockopt_posix.go index 37c62871..34dfed58 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_stub.go b/vendor/golang.org/x/net/ipv6/sockopt_stub.go index 32fd8664..a09c3aaf 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_aix.go b/vendor/golang.org/x/net/ipv6/sys_aix.go index a47182af..93c8efc4 100644 --- a/vendor/golang.org/x/net/ipv6/sys_aix.go +++ b/vendor/golang.org/x/net/ipv6/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq.go b/vendor/golang.org/x/net/ipv6/sys_asmreq.go index 6ff9950d..5c9cb444 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go index 485290cb..dc704946 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf.go b/vendor/golang.org/x/net/ipv6/sys_bpf.go index b5661fb8..e39f75f4 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go index cb006618..8532a8f5 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bsd.go b/vendor/golang.org/x/net/ipv6/sys_bsd.go index bde41a6c..9f3bc2af 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv6/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || netbsd || openbsd -// +build dragonfly netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go index 023488a4..b40f5c68 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || solaris || zos -// +build aix darwin freebsd linux solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go index acdf2e5c..6526aad5 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !freebsd && !linux && !solaris && !zos -// +build !aix,!darwin,!freebsd,!linux,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_stub.go b/vendor/golang.org/x/net/ipv6/sys_stub.go index 5807bba3..76602c34 100644 --- a/vendor/golang.org/x/net/ipv6/sys_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go index f604b0f3..668716df 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go index 598fbfa0..6a53284d 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go index d4f78e40..13b34720 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv6 diff --git a/vendor/golang.org/x/sync/singleflight/singleflight.go b/vendor/golang.org/x/sync/singleflight/singleflight.go index 8473fb79..40518309 100644 --- a/vendor/golang.org/x/sync/singleflight/singleflight.go +++ b/vendor/golang.org/x/sync/singleflight/singleflight.go @@ -31,6 +31,15 @@ func (p *panicError) Error() string { return fmt.Sprintf("%v\n\n%s", p.value, p.stack) } +func (p *panicError) Unwrap() error { + err, ok := p.value.(error) + if !ok { + return nil + } + + return err +} + func newPanicError(v interface{}) error { stack := debug.Stack() diff --git a/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s index db9171c2..269e173c 100644 --- a/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s +++ b/vendor/golang.org/x/sys/cpu/asm_aix_ppc64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/cpu/cpu_aix.go b/vendor/golang.org/x/sys/cpu/cpu_aix.go index 8aaeef54..9bf0c32e 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_aix.go +++ b/vendor/golang.org/x/sys/cpu/cpu_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_arm64.s index c61f95a0..fcb9a388 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.s +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go index ccf542a7..a8acd3e3 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go index 0af2f248..c8ae6ddc 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go index fa7cdb9b..910728fb 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (386 || amd64 || amd64p32) && gc -// +build 386 amd64 amd64p32 -// +build gc package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go index 2aff3189..7f194678 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo -// +build gccgo package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go index 4bfbda61..9526d2ce 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo -// +build gccgo package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c index 6cc73109..3f73a05d 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (386 || amd64 || amd64p32) && gccgo -// +build 386 amd64 amd64p32 -// +build gccgo #include #include diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go index 863d415a..99c60fe9 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (386 || amd64 || amd64p32) && gccgo -// +build 386 amd64 amd64p32 -// +build gccgo package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux.go b/vendor/golang.org/x/sys/cpu/cpu_linux.go index 159a686f..743eb543 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !386 && !amd64 && !amd64p32 && !arm64 -// +build !386,!amd64,!amd64p32,!arm64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go index 6000db4c..4686c1d5 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go index f4992b1a..cd63e733 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x -// +build linux,!arm,!arm64,!mips64,!mips64le,!ppc64,!ppc64le,!s390x package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go index 021356d6..197188e6 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_loong64.go index 0f57b05b..55863585 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_loong64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 -// +build loong64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go index f4063c66..fedb00cc 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_mips64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_mipsx.go b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go index 07c4e36d..ffb4ec7e 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_mipsx.go +++ b/vendor/golang.org/x/sys/cpu/cpu_mipsx.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go index d7b4fb4c..e9ecf2a4 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_arm.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux && arm -// +build !linux,arm package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go index f3cde129..5341e7f8 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux && !netbsd && !openbsd && arm64 -// +build !linux,!netbsd,!openbsd,arm64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go index 0dafe964..5f8f2419 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux && (mips64 || mips64le) -// +build !linux -// +build mips64 mips64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go index 060d46b6..89608fba 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_ppc64x.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !linux && (ppc64 || ppc64le) -// +build !aix -// +build !linux -// +build ppc64 ppc64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go index dd10eb79..5ab87808 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux && riscv64 -// +build !linux,riscv64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go index 4e8acd16..c14f12b1 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go +++ b/vendor/golang.org/x/sys/cpu/cpu_ppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go index ff7da60e..7f0c79c0 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 -// +build riscv64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_s390x.s b/vendor/golang.org/x/sys/cpu/cpu_s390x.s index 96f81e20..1fb4b701 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_s390x.s +++ b/vendor/golang.org/x/sys/cpu/cpu_s390x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/cpu/cpu_wasm.go b/vendor/golang.org/x/sys/cpu/cpu_wasm.go index 7747d888..384787ea 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_wasm.go +++ b/vendor/golang.org/x/sys/cpu/cpu_wasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build wasm -// +build wasm package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.go b/vendor/golang.org/x/sys/cpu/cpu_x86.go index 2dcde828..c29f5e4c 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 || amd64p32 -// +build 386 amd64 amd64p32 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.s b/vendor/golang.org/x/sys/cpu/cpu_x86.s index 39acab2f..7d7ba33e 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.s +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (386 || amd64 || amd64p32) && gc -// +build 386 amd64 amd64p32 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/cpu/endian_big.go b/vendor/golang.org/x/sys/cpu/endian_big.go index 93ce03a3..7fe04b0a 100644 --- a/vendor/golang.org/x/sys/cpu/endian_big.go +++ b/vendor/golang.org/x/sys/cpu/endian_big.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 -// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/endian_little.go b/vendor/golang.org/x/sys/cpu/endian_little.go index 55db853e..48eccc4c 100644 --- a/vendor/golang.org/x/sys/cpu/endian_little.go +++ b/vendor/golang.org/x/sys/cpu/endian_little.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm -// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh wasm package cpu diff --git a/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go index d87bd6b3..4cd64c70 100644 --- a/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go +++ b/vendor/golang.org/x/sys/cpu/proc_cpuinfo_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && arm64 -// +build linux,arm64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go index b975ea2a..4c9788ea 100644 --- a/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go +++ b/vendor/golang.org/x/sys/cpu/runtime_auxv_go121.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.21 -// +build go1.21 package cpu diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go index 96134157..1b9ccb09 100644 --- a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go @@ -9,7 +9,6 @@ // gccgo's libgo and thus must not used a CGo method. //go:build aix && gccgo -// +build aix,gccgo package cpu diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go index 904be42f..e8b6cdbe 100644 --- a/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_ppc64_gc.go @@ -7,7 +7,6 @@ // (See golang.org/issue/32102) //go:build aix && ppc64 && gc -// +build aix,ppc64,gc package cpu diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go index 2000064a..5627d70e 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go118.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go118.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.19 -// +build !go1.19 package execabs diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go index f364b341..d60ab1b4 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go119.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go119.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.19 -// +build go1.19 package execabs diff --git a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go index c9b69937..73687de7 100644 --- a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go +++ b/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.5 -// +build go1.5 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/pwd_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_plan9.go index 98bf56b7..fb945821 100644 --- a/vendor/golang.org/x/sys/plan9/pwd_plan9.go +++ b/vendor/golang.org/x/sys/plan9/pwd_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.5 -// +build !go1.5 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/race.go b/vendor/golang.org/x/sys/plan9/race.go index 62377d2f..c02d9ed3 100644 --- a/vendor/golang.org/x/sys/plan9/race.go +++ b/vendor/golang.org/x/sys/plan9/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 && race -// +build plan9,race package plan9 diff --git a/vendor/golang.org/x/sys/plan9/race0.go b/vendor/golang.org/x/sys/plan9/race0.go index f8da3087..7b15e15f 100644 --- a/vendor/golang.org/x/sys/plan9/race0.go +++ b/vendor/golang.org/x/sys/plan9/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 && !race -// +build plan9,!race package plan9 diff --git a/vendor/golang.org/x/sys/plan9/str.go b/vendor/golang.org/x/sys/plan9/str.go index 55fa8d02..ba3e8ff8 100644 --- a/vendor/golang.org/x/sys/plan9/str.go +++ b/vendor/golang.org/x/sys/plan9/str.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/syscall.go b/vendor/golang.org/x/sys/plan9/syscall.go index 67e5b011..d631fd66 100644 --- a/vendor/golang.org/x/sys/plan9/syscall.go +++ b/vendor/golang.org/x/sys/plan9/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 // Package plan9 contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go index 3f40b9bd..f780d5c8 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && 386 -// +build plan9,386 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go index 0e6a96aa..7de61065 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && amd64 -// +build plan9,amd64 package plan9 diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go index 244c501b..ea85780f 100644 --- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go +++ b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build plan9 && arm -// +build plan9,arm package plan9 diff --git a/vendor/golang.org/x/sys/unix/aliases.go b/vendor/golang.org/x/sys/unix/aliases.go index abc89c10..e7d3df4b 100644 --- a/vendor/golang.org/x/sys/unix/aliases.go +++ b/vendor/golang.org/x/sys/unix/aliases.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9 -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos -// +build go1.9 package unix diff --git a/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s b/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s index db9171c2..269e173c 100644 --- a/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s +++ b/vendor/golang.org/x/sys/unix/asm_aix_ppc64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_386.s b/vendor/golang.org/x/sys/unix/asm_bsd_386.s index e0fcd9b3..a4fcef0e 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_386.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_386.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd || netbsd || openbsd) && gc -// +build freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s b/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s index 2b99c349..1e63615c 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_amd64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc -// +build darwin dragonfly freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_arm.s b/vendor/golang.org/x/sys/unix/asm_bsd_arm.s index d702d4ad..6496c310 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_arm.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_arm.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd || netbsd || openbsd) && gc -// +build freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s b/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s index fe36a739..4fd1f54d 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_arm64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s b/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s index e5b9a848..42f7eb9e 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s index d560019e..f8902667 100644 --- a/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || freebsd || netbsd || openbsd) && gc -// +build darwin freebsd netbsd openbsd -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_386.s b/vendor/golang.org/x/sys/unix/asm_linux_386.s index 8fd101d0..3b473487 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_386.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s b/vendor/golang.org/x/sys/unix/asm_linux_amd64.s index 7ed38e43..67e29f31 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm.s b/vendor/golang.org/x/sys/unix/asm_linux_arm.s index 8ef1d514..d6ae269c 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_arm.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_arm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s b/vendor/golang.org/x/sys/unix/asm_linux_arm64.s index 98ae0276..01e5e253 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_arm64.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && arm64 && gc -// +build linux -// +build arm64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s index 56535728..2abf12f6 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && loong64 && gc -// +build linux -// +build loong64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s b/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s index 21231d2c..f84bae71 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) && gc -// +build linux -// +build mips64 mips64le -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s b/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s index 6783b26c..f08f6280 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) && gc -// +build linux -// +build mips mipsle -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s index 19d49893..bdfc024d 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) && gc -// +build linux -// +build ppc64 ppc64le -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s b/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s index e42eb81d..2e8c9961 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_riscv64.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && gc -// +build riscv64 -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s b/vendor/golang.org/x/sys/unix/asm_linux_s390x.s index c46aab33..2c394b11 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_s390x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && s390x && gc -// +build linux -// +build s390x -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s index 5e7a1169..fab586a2 100644 --- a/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s b/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s index f8c5394c..f949ec54 100644 --- a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s +++ b/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/asm_zos_s390x.s b/vendor/golang.org/x/sys/unix/asm_zos_s390x.s index 3b54e185..2f67ba86 100644 --- a/vendor/golang.org/x/sys/unix/asm_zos_s390x.s +++ b/vendor/golang.org/x/sys/unix/asm_zos_s390x.s @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x && gc -// +build zos -// +build s390x -// +build gc #include "textflag.h" diff --git a/vendor/golang.org/x/sys/unix/cap_freebsd.go b/vendor/golang.org/x/sys/unix/cap_freebsd.go index 0b7c6adb..a0865789 100644 --- a/vendor/golang.org/x/sys/unix/cap_freebsd.go +++ b/vendor/golang.org/x/sys/unix/cap_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd -// +build freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/constants.go b/vendor/golang.org/x/sys/unix/constants.go index 394a3965..6fb7cb77 100644 --- a/vendor/golang.org/x/sys/unix/constants.go +++ b/vendor/golang.org/x/sys/unix/constants.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/dev_aix_ppc.go b/vendor/golang.org/x/sys/unix/dev_aix_ppc.go index 65a99850..d7851346 100644 --- a/vendor/golang.org/x/sys/unix/dev_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/dev_aix_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc -// +build aix,ppc // Functions to access/create device major and minor numbers matching the // encoding used by AIX. diff --git a/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go b/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go index 8fc08ad0..623a5e69 100644 --- a/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/dev_aix_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc64 -// +build aix,ppc64 // Functions to access/create device major and minor numbers matching the // encoding used AIX. diff --git a/vendor/golang.org/x/sys/unix/dev_zos.go b/vendor/golang.org/x/sys/unix/dev_zos.go index a388e59a..bb6a64fe 100644 --- a/vendor/golang.org/x/sys/unix/dev_zos.go +++ b/vendor/golang.org/x/sys/unix/dev_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Functions to access/create device major and minor numbers matching the // encoding used by z/OS. diff --git a/vendor/golang.org/x/sys/unix/dirent.go b/vendor/golang.org/x/sys/unix/dirent.go index 2499f977..1ebf1178 100644 --- a/vendor/golang.org/x/sys/unix/dirent.go +++ b/vendor/golang.org/x/sys/unix/dirent.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/endian_big.go b/vendor/golang.org/x/sys/unix/endian_big.go index a5202655..1095fd31 100644 --- a/vendor/golang.org/x/sys/unix/endian_big.go +++ b/vendor/golang.org/x/sys/unix/endian_big.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 -// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/endian_little.go b/vendor/golang.org/x/sys/unix/endian_little.go index b0f2bc4a..b9f0e277 100644 --- a/vendor/golang.org/x/sys/unix/endian_little.go +++ b/vendor/golang.org/x/sys/unix/endian_little.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh -// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh package unix diff --git a/vendor/golang.org/x/sys/unix/env_unix.go b/vendor/golang.org/x/sys/unix/env_unix.go index 29ccc4d1..a96da71f 100644 --- a/vendor/golang.org/x/sys/unix/env_unix.go +++ b/vendor/golang.org/x/sys/unix/env_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Unix environment variables. diff --git a/vendor/golang.org/x/sys/unix/epoll_zos.go b/vendor/golang.org/x/sys/unix/epoll_zos.go index cedaf7e0..7753fdde 100644 --- a/vendor/golang.org/x/sys/unix/epoll_zos.go +++ b/vendor/golang.org/x/sys/unix/epoll_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/fcntl.go b/vendor/golang.org/x/sys/unix/fcntl.go index e9b99125..58c6bfc7 100644 --- a/vendor/golang.org/x/sys/unix/fcntl.go +++ b/vendor/golang.org/x/sys/unix/fcntl.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go b/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go index 29d44808..13b4acd5 100644 --- a/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go +++ b/vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc) -// +build linux,386 linux,arm linux,mips linux,mipsle linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/fdset.go b/vendor/golang.org/x/sys/unix/fdset.go index a8068f94..9e83d18c 100644 --- a/vendor/golang.org/x/sys/unix/fdset.go +++ b/vendor/golang.org/x/sys/unix/fdset.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/fstatfs_zos.go b/vendor/golang.org/x/sys/unix/fstatfs_zos.go index e377cc9f..c8bde601 100644 --- a/vendor/golang.org/x/sys/unix/fstatfs_zos.go +++ b/vendor/golang.org/x/sys/unix/fstatfs_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/gccgo.go b/vendor/golang.org/x/sys/unix/gccgo.go index b06f52d7..aca5721d 100644 --- a/vendor/golang.org/x/sys/unix/gccgo.go +++ b/vendor/golang.org/x/sys/unix/gccgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && !aix && !hurd -// +build gccgo,!aix,!hurd package unix diff --git a/vendor/golang.org/x/sys/unix/gccgo_c.c b/vendor/golang.org/x/sys/unix/gccgo_c.c index f98a1c54..d468b7b4 100644 --- a/vendor/golang.org/x/sys/unix/gccgo_c.c +++ b/vendor/golang.org/x/sys/unix/gccgo_c.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && !aix && !hurd -// +build gccgo,!aix,!hurd #include #include diff --git a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go b/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go index e60e49a3..972d61bd 100644 --- a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo && linux && amd64 -// +build gccgo,linux,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/ifreq_linux.go b/vendor/golang.org/x/sys/unix/ifreq_linux.go index 15721a51..848840ae 100644 --- a/vendor/golang.org/x/sys/unix/ifreq_linux.go +++ b/vendor/golang.org/x/sys/unix/ifreq_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_signed.go b/vendor/golang.org/x/sys/unix/ioctl_signed.go index 7def9580..5b0759bd 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_signed.go +++ b/vendor/golang.org/x/sys/unix/ioctl_signed.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || solaris -// +build aix solaris package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_unsigned.go b/vendor/golang.org/x/sys/unix/ioctl_unsigned.go index 649913d1..20f470b9 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_unsigned.go +++ b/vendor/golang.org/x/sys/unix/ioctl_unsigned.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd hurd linux netbsd openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_zos.go b/vendor/golang.org/x/sys/unix/ioctl_zos.go index cdc21bf7..c8b2a750 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_zos.go +++ b/vendor/golang.org/x/sys/unix/ioctl_zos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 47fa6a7e..cbe24150 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -663,7 +663,6 @@ echo '// mkerrors.sh' "$@" echo '// Code generated by the command above; see README.md. DO NOT EDIT.' echo echo "//go:build ${GOARCH} && ${GOOS}" -echo "// +build ${GOARCH},${GOOS}" echo go tool cgo -godefs -- "$@" _const.go >_error.out cat _error.out | grep -vf _error.grep | grep -vf _signal.grep diff --git a/vendor/golang.org/x/sys/unix/mmap_nomremap.go b/vendor/golang.org/x/sys/unix/mmap_nomremap.go index ca051363..4b68e597 100644 --- a/vendor/golang.org/x/sys/unix/mmap_nomremap.go +++ b/vendor/golang.org/x/sys/unix/mmap_nomremap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || openbsd || solaris -// +build aix darwin dragonfly freebsd openbsd solaris package unix diff --git a/vendor/golang.org/x/sys/unix/mremap.go b/vendor/golang.org/x/sys/unix/mremap.go index fa93d0aa..fd45fe52 100644 --- a/vendor/golang.org/x/sys/unix/mremap.go +++ b/vendor/golang.org/x/sys/unix/mremap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux || netbsd -// +build linux netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/pagesize_unix.go b/vendor/golang.org/x/sys/unix/pagesize_unix.go index 53f1b4c5..4d0a3430 100644 --- a/vendor/golang.org/x/sys/unix/pagesize_unix.go +++ b/vendor/golang.org/x/sys/unix/pagesize_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris // For Unix, get the pagesize from the runtime. diff --git a/vendor/golang.org/x/sys/unix/pledge_openbsd.go b/vendor/golang.org/x/sys/unix/pledge_openbsd.go index eb48294b..6a09af53 100644 --- a/vendor/golang.org/x/sys/unix/pledge_openbsd.go +++ b/vendor/golang.org/x/sys/unix/pledge_openbsd.go @@ -8,54 +8,31 @@ import ( "errors" "fmt" "strconv" - "syscall" - "unsafe" ) // Pledge implements the pledge syscall. // -// The pledge syscall does not accept execpromises on OpenBSD releases -// before 6.3. -// -// execpromises must be empty when Pledge is called on OpenBSD -// releases predating 6.3, otherwise an error will be returned. +// This changes both the promises and execpromises; use PledgePromises or +// PledgeExecpromises to only change the promises or execpromises +// respectively. // // For more information see pledge(2). func Pledge(promises, execpromises string) error { - maj, min, err := majmin() - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) + pptr, err := BytePtrFromString(promises) if err != nil { return err } - pptr, err := syscall.BytePtrFromString(promises) + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - // This variable will hold either a nil unsafe.Pointer or - // an unsafe.Pointer to a string (execpromises). - var expr unsafe.Pointer - - // If we're running on OpenBSD > 6.2, pass execpromises to the syscall. - if maj > 6 || (maj == 6 && min > 2) { - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - expr = unsafe.Pointer(exptr) - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, exptr) } // PledgePromises implements the pledge syscall. @@ -64,30 +41,16 @@ func Pledge(promises, execpromises string) error { // // For more information see pledge(2). func PledgePromises(promises string) error { - maj, min, err := majmin() - if err != nil { - return err - } - - err = pledgeAvailable(maj, min, "") - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - // This variable holds the execpromises and is always nil. - var expr unsafe.Pointer - - pptr, err := syscall.BytePtrFromString(promises) + pptr, err := BytePtrFromString(promises) if err != nil { return err } - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, nil) } // PledgeExecpromises implements the pledge syscall. @@ -96,30 +59,16 @@ func PledgePromises(promises string) error { // // For more information see pledge(2). func PledgeExecpromises(execpromises string) error { - maj, min, err := majmin() - if err != nil { + if err := pledgeAvailable(); err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - // This variable holds the promises and is always nil. - var pptr unsafe.Pointer - - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0) - if e != 0 { - return e - } - - return nil + return pledge(nil, exptr) } // majmin returns major and minor version number for an OpenBSD system. @@ -147,16 +96,15 @@ func majmin() (major int, minor int, err error) { // pledgeAvailable checks for availability of the pledge(2) syscall // based on the running OpenBSD version. -func pledgeAvailable(maj, min int, execpromises string) error { - // If OpenBSD <= 5.9, pledge is not available. - if (maj == 5 && min != 9) || maj < 5 { - return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min) +func pledgeAvailable() error { + maj, min, err := majmin() + if err != nil { + return err } - // If OpenBSD <= 6.2 and execpromises is not empty, - // return an error - execpromises is not available before 6.3 - if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" { - return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min) + // Require OpenBSD 6.4 as a minimum. + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Pledge on OpenBSD %d.%d", maj, min) } return nil diff --git a/vendor/golang.org/x/sys/unix/ptrace_darwin.go b/vendor/golang.org/x/sys/unix/ptrace_darwin.go index 463c3eff..3f0975f3 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_darwin.go +++ b/vendor/golang.org/x/sys/unix/ptrace_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin && !ios -// +build darwin,!ios package unix diff --git a/vendor/golang.org/x/sys/unix/ptrace_ios.go b/vendor/golang.org/x/sys/unix/ptrace_ios.go index ed0509a0..a4d35db5 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_ios.go +++ b/vendor/golang.org/x/sys/unix/ptrace_ios.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ios -// +build ios package unix diff --git a/vendor/golang.org/x/sys/unix/race.go b/vendor/golang.org/x/sys/unix/race.go index 6f6c5fec..714d2aae 100644 --- a/vendor/golang.org/x/sys/unix/race.go +++ b/vendor/golang.org/x/sys/unix/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin && race) || (linux && race) || (freebsd && race) -// +build darwin,race linux,race freebsd,race package unix diff --git a/vendor/golang.org/x/sys/unix/race0.go b/vendor/golang.org/x/sys/unix/race0.go index 706e1322..4a9f6634 100644 --- a/vendor/golang.org/x/sys/unix/race0.go +++ b/vendor/golang.org/x/sys/unix/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || (darwin && !race) || (linux && !race) || (freebsd && !race) || netbsd || openbsd || solaris || dragonfly || zos -// +build aix darwin,!race linux,!race freebsd,!race netbsd openbsd solaris dragonfly zos package unix diff --git a/vendor/golang.org/x/sys/unix/readdirent_getdents.go b/vendor/golang.org/x/sys/unix/readdirent_getdents.go index 4d625756..dbd2b6cc 100644 --- a/vendor/golang.org/x/sys/unix/readdirent_getdents.go +++ b/vendor/golang.org/x/sys/unix/readdirent_getdents.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd -// +build aix dragonfly freebsd linux netbsd openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go b/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go index 2a4ba47c..130398b6 100644 --- a/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go +++ b/vendor/golang.org/x/sys/unix/readdirent_getdirentries.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package unix diff --git a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go index 3865943f..c3a62dbb 100644 --- a/vendor/golang.org/x/sys/unix/sockcmsg_unix.go +++ b/vendor/golang.org/x/sys/unix/sockcmsg_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Socket control messages diff --git a/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go b/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go index 0840fe4a..4a1eab37 100644 --- a/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go +++ b/vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/syscall.go b/vendor/golang.org/x/sys/unix/syscall.go index 63e8c838..5ea74da9 100644 --- a/vendor/golang.org/x/sys/unix/syscall.go +++ b/vendor/golang.org/x/sys/unix/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Package unix contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index e94e6cda..67ce6cef 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix // Aix system calls. // This file is compiled as ordinary Go code, @@ -107,7 +106,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go index f2871fa9..1fdaa476 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc -// +build aix,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go index 75718ec0..c87f9a9f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix && ppc64 -// +build aix,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index 4217de51..6f328e3a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd // BSD system call wrappers shared by *BSD based systems // including OS X (Darwin) and FreeBSD. Like the other diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go index b37310ce..0eaecf5f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go index d51ec996..f36c6707 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go index 53c96641..16dc6993 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin && go1.12 -// +build darwin,go1.12 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go index 4e2d3212..14bab6b2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go index b8da5100..3967bca7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go index 47155c48..eff19ada 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go index 08932093..4f24b517 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go index d151a0d0..ac30759e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go index d5cd64b3..aab725ca 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd.go b/vendor/golang.org/x/sys/unix/syscall_hurd.go index 381fd467..ba46651f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build hurd -// +build hurd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd_386.go b/vendor/golang.org/x/sys/unix/syscall_hurd_386.go index 7cf54a3e..df89f9e6 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && hurd -// +build 386,hurd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_illumos.go b/vendor/golang.org/x/sys/unix/syscall_illumos.go index 87db5a6a..a863f705 100644 --- a/vendor/golang.org/x/sys/unix/syscall_illumos.go +++ b/vendor/golang.org/x/sys/unix/syscall_illumos.go @@ -5,7 +5,6 @@ // illumos system calls not present on Solaris. //go:build amd64 && illumos -// +build amd64,illumos package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index fb4e5022..a5e1c10e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -417,7 +417,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -2482,3 +2483,5 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { } return attr, nil } + +//sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_386.go index c7d9945e..506dafa7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && linux -// +build 386,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go index 08086ac6..38d55641 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (386 || amd64 || mips || mipsle || mips64 || mipsle || ppc64 || ppc64le || ppc || s390x || sparc64) -// +build linux -// +build 386 amd64 mips mipsle mips64 mipsle ppc64 ppc64le ppc s390x sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index 70601ce3..d557cf8d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && linux -// +build amd64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go index 8b0f0f3a..facdb83b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && linux && gc -// +build amd64,linux,gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index da298641..cd2dd797 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && linux -// +build arm,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index f5266689..cf2ee6c7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && linux -// +build arm64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc.go index 2b1168d7..ffc4c2b6 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gc -// +build linux,gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go index 9843fb48..9ebfdcf4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gc && 386 -// +build linux,gc,386 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go index a6008fcc..5f2b57c4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && gc && linux -// +build arm,gc,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go index 7740af24..d1a3ad82 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gccgo && 386 -// +build linux,gccgo,386 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go index e16a1229..f2f67423 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && gccgo && arm -// +build linux,gccgo,arm package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index f6ab02ec..3d0e9845 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 && linux -// +build loong64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index 93fe59d2..70963a95 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go index aae7f0ff..c218ebd2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go index 66eff19a..e6c48500 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && ppc -// +build linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index 806aa257..7286a9aa 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 5e6ceee1..6f5a2889 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && linux -// +build riscv64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go index 2f89e8f5..66f31210 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build s390x && linux -// +build s390x,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go index 7ca064ae..11d1f169 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build sparc64 && linux -// +build sparc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go index 5199d282..7a5eb574 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go index 70a9c52e..62d8957a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go index 3eb5942f..ce6a0688 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go index fc6ccfd8..d46d689d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 6f34479b..d2882ee0 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -137,18 +137,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { - var _p0 unsafe.Pointer + var bufptr *Statfs_t var bufsize uintptr if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + bufptr = &buf[0] bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) } - r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) - n = int(r0) - if e1 != 0 { - err = e1 - } - return + return getfsstat(bufptr, bufsize, flags) } //sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) @@ -326,4 +321,7 @@ func Uname(uname *Utsname) error { //sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) +//sys getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) //sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) +//sys pledge(promises *byte, execpromises *byte) (err error) +//sys unveil(path *byte, flags *byte) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go index 6baabcdc..9ddc89f4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go index bab25360..70a3c96e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go index 8eed3c4d..265caa87 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go index 483dde99..ac4fda17 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go index 04aa43f4..0a451e6d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd -// +build openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go index c2796139..30a308cb 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go index 23199a7f..ea954330 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index b99cfa13..60c8142d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -128,7 +128,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go index 0bd25ef8..e02d8cea 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index f6eda270..77081de8 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go index b6919ca5..05c95bcc 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (linux && !ppc64 && !ppc64le) || netbsd || openbsd || solaris) && gc -// +build darwin dragonfly freebsd linux,!ppc64,!ppc64le netbsd openbsd solaris -// +build gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go index f6f707ac..23f39b7a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64le || ppc64) && gc -// +build linux -// +build ppc64le ppc64 -// +build gc package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index 4596d041..d99d05f1 100644 --- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/sysvshm_linux.go b/vendor/golang.org/x/sys/unix/sysvshm_linux.go index 2c3a4437..4fcd38de 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_linux.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package unix diff --git a/vendor/golang.org/x/sys/unix/sysvshm_unix.go b/vendor/golang.org/x/sys/unix/sysvshm_unix.go index 5bb41d17..79a84f18 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_unix.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin && !ios) || linux -// +build darwin,!ios linux package unix diff --git a/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go b/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go index 71bddefd..9eb0db66 100644 --- a/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go +++ b/vendor/golang.org/x/sys/unix/sysvshm_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin && !ios -// +build darwin,!ios package unix diff --git a/vendor/golang.org/x/sys/unix/timestruct.go b/vendor/golang.org/x/sys/unix/timestruct.go index 616b1b28..7997b190 100644 --- a/vendor/golang.org/x/sys/unix/timestruct.go +++ b/vendor/golang.org/x/sys/unix/timestruct.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package unix diff --git a/vendor/golang.org/x/sys/unix/unveil_openbsd.go b/vendor/golang.org/x/sys/unix/unveil_openbsd.go index 168d5ae7..cb7e598c 100644 --- a/vendor/golang.org/x/sys/unix/unveil_openbsd.go +++ b/vendor/golang.org/x/sys/unix/unveil_openbsd.go @@ -4,39 +4,48 @@ package unix -import ( - "syscall" - "unsafe" -) +import "fmt" // Unveil implements the unveil syscall. // For more information see unveil(2). // Note that the special case of blocking further // unveil calls is handled by UnveilBlock. func Unveil(path string, flags string) error { - pathPtr, err := syscall.BytePtrFromString(path) - if err != nil { + if err := supportsUnveil(); err != nil { return err } - flagsPtr, err := syscall.BytePtrFromString(flags) + pathPtr, err := BytePtrFromString(path) if err != nil { return err } - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0) - if e != 0 { - return e + flagsPtr, err := BytePtrFromString(flags) + if err != nil { + return err } - return nil + return unveil(pathPtr, flagsPtr) } // UnveilBlock blocks future unveil calls. // For more information see unveil(2). func UnveilBlock() error { - // Both pointers must be nil. - var pathUnsafe, flagsUnsafe unsafe.Pointer - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(pathUnsafe), uintptr(flagsUnsafe), 0) - if e != 0 { - return e + if err := supportsUnveil(); err != nil { + return err } + return unveil(nil, nil) +} + +// supportsUnveil checks for availability of the unveil(2) system call based +// on the running OpenBSD version. +func supportsUnveil() error { + maj, min, err := majmin() + if err != nil { + return err + } + + // unveil is not available before 6.4 + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Unveil on OpenBSD %d.%d", maj, min) + } + return nil } diff --git a/vendor/golang.org/x/sys/unix/xattr_bsd.go b/vendor/golang.org/x/sys/unix/xattr_bsd.go index f5f8e9f3..e1687939 100644 --- a/vendor/golang.org/x/sys/unix/xattr_bsd.go +++ b/vendor/golang.org/x/sys/unix/xattr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || netbsd -// +build freebsd netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go index ca9799b7..2fb219d7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && aix -// +build ppc,aix // Created by cgo -godefs - DO NOT EDIT // cgo -godefs -- -maix32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go index 200c8c26..b0e6f5c8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && aix -// +build ppc64,aix // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -maix64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go index 14300762..e40fa852 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go index ab044a74..bb02aa6c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go index 17bba0e4..c0e0f869 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go index f8c2c513..6c692390 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go index 96310c3b..dd9163f8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go index 777b69de..493a2a79 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go index c557ac2d..8b437b30 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go index 341b4d96..67c02dd5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index f9c7f479..9c00cbf5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -481,10 +480,14 @@ const ( BPF_FROM_BE = 0x8 BPF_FROM_LE = 0x0 BPF_FS_MAGIC = 0xcafe4a11 + BPF_F_AFTER = 0x10 BPF_F_ALLOW_MULTI = 0x2 BPF_F_ALLOW_OVERRIDE = 0x1 BPF_F_ANY_ALIGNMENT = 0x2 - BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_BEFORE = 0x8 + BPF_F_ID = 0x20 + BPF_F_LINK = 0x2000 + BPF_F_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 @@ -521,6 +524,7 @@ const ( BPF_MAJOR_VERSION = 0x1 BPF_MAXINSNS = 0x1000 BPF_MEM = 0x60 + BPF_MEMSX = 0x80 BPF_MEMWORDS = 0x10 BPF_MINOR_VERSION = 0x1 BPF_MISC = 0x7 @@ -776,6 +780,8 @@ const ( DEVLINK_GENL_MCGRP_CONFIG_NAME = "config" DEVLINK_GENL_NAME = "devlink" DEVLINK_GENL_VERSION = 0x1 + DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO = 0x4 + DEVLINK_PORT_FN_CAP_IPSEC_PACKET = 0x8 DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2 DEVLINK_PORT_FN_CAP_ROCE = 0x1 DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14 @@ -1698,6 +1704,7 @@ const ( KEXEC_ON_CRASH = 0x1 KEXEC_PRESERVE_CONTEXT = 0x2 KEXEC_SEGMENT_MAX = 0x10 + KEXEC_UPDATE_ELFCOREHDR = 0x4 KEYCTL_ASSUME_AUTHORITY = 0x10 KEYCTL_CAPABILITIES = 0x1f KEYCTL_CAPS0_BIG_KEY = 0x10 @@ -2275,6 +2282,7 @@ const ( PERF_MEM_LVLNUM_PMEM = 0xe PERF_MEM_LVLNUM_RAM = 0xd PERF_MEM_LVLNUM_SHIFT = 0x21 + PERF_MEM_LVLNUM_UNC = 0x8 PERF_MEM_LVL_HIT = 0x2 PERF_MEM_LVL_IO = 0x1000 PERF_MEM_LVL_L1 = 0x8 @@ -3461,6 +3469,7 @@ const ( XDP_PACKET_HEADROOM = 0x100 XDP_PGOFF_RX_RING = 0x0 XDP_PGOFF_TX_RING = 0x80000000 + XDP_PKT_CONTD = 0x1 XDP_RING_NEED_WAKEUP = 0x1 XDP_RX_RING = 0x2 XDP_SHARED_UMEM = 0x1 @@ -3473,6 +3482,7 @@ const ( XDP_UMEM_REG = 0x4 XDP_UMEM_UNALIGNED_CHUNK_FLAG = 0x1 XDP_USE_NEED_WAKEUP = 0x8 + XDP_USE_SG = 0x10 XDP_ZEROCOPY = 0x4 XENFS_SUPER_MAGIC = 0xabba1974 XFS_SUPER_MAGIC = 0x58465342 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 30aee00a..4920821c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/386/include -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 8ebfa512..a0c1e411 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/amd64/include -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 271a21cd..c6398556 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/arm/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 910c330a..47cc62e2 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index a640798c..27ac4a09 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/loong64/include _const.go @@ -119,6 +118,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 LASX_CTX_MAGIC = 0x41535801 + LBT_CTX_MAGIC = 0x42540001 LSX_CTX_MAGIC = 0x53580001 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 0d5925d3..54694642 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index d72a00e0..3adb81d7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips64/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 02ba129f..2dfe98f0 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mips64le/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 8daa6dd9..f5398f84 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/mipsle/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 63c8fa2f..c54f152d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 930799ec..76057dc7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 8605a7dd..e0c3725e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64le/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 95a016f1..18f2813e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/riscv64/include _const.go @@ -228,6 +227,9 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTRACE_GETFDPIC = 0x21 + PTRACE_GETFDPIC_EXEC = 0x0 + PTRACE_GETFDPIC_INTERP = 0x1 RLIMIT_AS = 0x9 RLIMIT_MEMLOCK = 0x8 RLIMIT_NOFILE = 0x7 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 1ae0108f..11619d4e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/s390x/include -fsigned-char _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 1bb7c633..396d994d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -Wall -Werror -static -I/tmp/sparc64/include _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go index 72f7420d..130085df 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go index 8d4eb0c0..84769a1a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go index 9eef9749..602ded00 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -marm _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go index 3b62ba19..efc0406e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go index af20e474..5a6500f8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m32 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go index 6015fcb2..a5aeeb97 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go index 8d44955e..0e9748a7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go index ae16fe75..4f4449ab 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go index 03d90fe3..76a363f0 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go index 8e2c51b1..43ca0cdf 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go index 13d40303..b1b8bb20 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go index 1afee6a0..d2ddd317 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && solaris -// +build amd64,solaris // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go index fc7d0506..4dfd2e05 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Hand edited based on zerrors_linux_s390x.go // TODO: auto-generate. diff --git a/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go b/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go index 97f20ca2..586317c7 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("arm", "arm64"). DO NOT EDIT. //go:build linux && (arm || arm64) -// +build linux -// +build arm arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go b/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go index 0b5f7943..d7c881be 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("mips", "mips64"). DO NOT EDIT. //go:build linux && (mips || mips64) -// +build linux -// +build mips mips64 package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go b/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go index 2807f7e6..2d2de5d2 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("mipsle", "mips64le"). DO NOT EDIT. //go:build linux && (mipsle || mips64le) -// +build linux -// +build mipsle mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go b/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go index 281ea64e..5adc79fb 100644 --- a/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go +++ b/vendor/golang.org/x/sys/unix/zptrace_x86_linux.go @@ -1,8 +1,6 @@ // Code generated by linux/mkall.go generatePtracePair("386", "amd64"). DO NOT EDIT. //go:build linux && (386 || amd64) -// +build linux -// +build 386 amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go index d1d1d233..6ea64a3c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc -// +build aix,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go index f99a18ad..99ee4399 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 -// +build aix,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go index c4d50ae5..b68a7836 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 && gc -// +build aix,ppc64,gc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go index 6903d3b0..0a87450b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build aix && ppc64 && gccgo -// +build aix,ppc64,gccgo package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 1cad561e..ccb02f24 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build darwin && amd64 -// +build darwin,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index b18edbd0..1b40b997 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build darwin && arm64 -// +build darwin,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go index 0c67df64..aad65fc7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build dragonfly && amd64 -// +build dragonfly,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index e6e05d14..c0096391 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && 386 -// +build freebsd,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index 7508acca..7664df74 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && amd64 -// +build freebsd,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index 7b56aead..ae099182 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && arm -// +build freebsd,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go index cc623dca..11fd5d45 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && arm64 -// +build freebsd,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go index 58184919..c3d2d653 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build freebsd && riscv64 -// +build freebsd,riscv64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go index 6be25cd1..c698cbc0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build illumos && amd64 -// +build illumos,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 1ff3aec7..faca7a55 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -2195,3 +2194,13 @@ func schedGetattr(pid int, attr *SchedAttr, size uint, flags uint) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) { + _, _, e1 := Syscall6(SYS_CACHESTAT, uintptr(fd), uintptr(unsafe.Pointer(crange)), uintptr(unsafe.Pointer(cstat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go index 07b549cc..4def3e9f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && 386 -// +build linux,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go index 5f481bf8..fef2bc8b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && amd64 -// +build linux,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go index 824cd52c..a9fd76a8 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && arm -// +build linux,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go index e77aecfe..46006502 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && arm64 -// +build linux,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go index 806ffd1e..c8987d26 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && loong64 -// +build linux,loong64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go index 961a3afb..921f4306 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips -// +build linux,mips package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go index ed05005e..44f06782 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips64 -// +build linux,mips64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go index d365b718..e7fa0abf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips64le -// +build linux,mips64le package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go index c3f1b8bb..8c512567 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mipsle -// +build linux,mipsle package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go index a6574cf9..7392fd45 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc -// +build linux,ppc package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go index f4099026..41180434 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64 -// +build linux,ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go index 9dfcc299..40c6ce7a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64le -// +build linux,ppc64le package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go index 0ab4f2ed..2cfe34ad 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && riscv64 -// +build linux,riscv64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go index 6cde3223..61e6f070 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && s390x -// +build linux,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go index 5253d65b..834b8420 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && sparc64 -// +build linux,sparc64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 2df3c5ba..e91ebc14 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && 386 -// +build netbsd,386 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index a60556ba..be28babb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && amd64 -// +build netbsd,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 9f788917..fb587e82 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && arm -// +build netbsd,arm package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go index 82a4cb2d..d576438b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build netbsd && arm64 -// +build netbsd,arm64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 66b3b645..88bfc288 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && 386 -// +build openbsd,386 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 3dcacd30..4cbeff17 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index c5c4cc11..b8a67b99 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && amd64 -// +build openbsd,amd64 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 2763620b..1123f275 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 93bfbb32..af50a65c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && arm -// +build openbsd,arm package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index c9223140..82badae3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index a107b8fd..8fb4ff36 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && arm64 -// +build openbsd,arm64 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index a6bc32c9..24d7eecb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index c427de50..f469a83e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && mips64 -// +build openbsd,mips64 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index b4e7bcea..9a498a06 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 60c1a99a..c26ca2e1 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && ppc64 -// +build openbsd,ppc64 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index ca3f7660..1f224aa4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -801,8 +801,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getfsstat(SB) + RET +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_utimensat(SB) RET GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_pledge(SB) + RET +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_unveil(SB) + RET +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 52eba360..bcc920dd 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build openbsd && riscv64 -// +build openbsd,riscv64 package unix @@ -2213,6 +2212,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2229,3 +2243,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 477a7d5b..87a79c70 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -668,7 +668,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index b4018946..829b87fe 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build solaris && amd64 -// +build solaris,amd64 package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go index 1d8fe1d4..94f01123 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go index 55e04847..3a58ae81 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go index d2243cf8..dcb7a0eb 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go index 82dc51bd..db5a7bf1 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go index cbdda1a4..7be575a7 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go index f55eae1a..d6e3174c 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go index e4405447..ee97157d 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go index a0db82fc..35c3b91d 100644 --- a/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go index f8298ff9..5edda768 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go index 5eb433bb..0dc9e8b4 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go index 703675c0..308ddf3a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go index 4e0d9610..418664e3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go index 01636b83..34d0b86d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go index ad99bc10..b71cf45e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go index 89dcc427..e32df1c1 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go index ee37aaa0..15ad6111 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 9862853d..fcf3ecbd 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux package unix @@ -448,4 +447,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 8901f0f4..f56dc250 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux package unix @@ -370,4 +369,6 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index 6902c37e..974bf246 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux package unix @@ -412,4 +411,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index a6d3dff8..39a2739e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux package unix @@ -315,4 +314,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index b18f3f71..cf9c9d77 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux package unix @@ -309,4 +308,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 0302e5e3..10b7362e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux package unix @@ -432,4 +431,5 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 6693ba4a..cd4d8b4f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux package unix @@ -362,4 +361,5 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index fd93f498..2c0efca8 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux package unix @@ -362,4 +361,5 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index 760ddcad..a72e31d3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux package unix @@ -432,4 +431,5 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index cff2b255..c7d1e374 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux package unix @@ -439,4 +438,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index a4b2405d..f4d4838c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux package unix @@ -411,4 +410,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index aca54b4e..b64f0e59 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux package unix @@ -411,4 +410,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 9d1738d6..95711195 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux package unix @@ -316,4 +315,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 022878dc..f94e943b 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux package unix @@ -377,4 +376,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 4100a761..ba0c2bc5 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux package unix @@ -390,4 +389,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go index 3a6699eb..b2aa8cd4 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go index 5677cd4f..524a1b1c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go index e784cb6d..d59b943a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go index bd4952ef..31e771d5 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go index 59773381..9fd77c6c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go index 16af2918..af10af28 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go index f59b18a9..cc2028af 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go index 721ef591..c06dd441 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go index 01c43a01..9ddbf3e0 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go index f258cfa2..19a6ee41 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go index 07919e0e..05192a78 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go index 073daad4..b2e30858 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go index 7a8161c1..3e6d57ca 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && aix -// +build ppc,aix package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go index 07ed733c..3a219bdc 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && aix -// +build ppc64,aix package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 690cefc3..091d107f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index 5bffc10e..28ff4ef7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go index d0ba8e9b..30e405bb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 29dc4833..6cbd094a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 0a89b289..7c03b6ee 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index c8666bb1..422107ee 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 88fb48a8..505a12ac 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go index 698dc975..cc986c79 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && freebsd -// +build riscv64,freebsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 18aa70b4..997bcd55 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -1,7 +1,6 @@ // Code generated by mkmerge; DO NOT EDIT. //go:build linux -// +build linux package unix @@ -5883,3 +5882,15 @@ type SchedAttr struct { } const SizeofSchedAttr = 0x38 + +type Cachestat_t struct { + Cache uint64 + Dirty uint64 + Writeback uint64 + Evicted uint64 + Recently_evicted uint64 +} +type CachestatRange struct { + Off uint64 + Len uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 6d8acbcc..438a30af 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux -// +build 386,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index 59293c68..adceca35 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 40cfa38c..eeaa00a3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux -// +build arm,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 055bc421..6739aa91 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go index f28affbc..9920ef63 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux -// +build loong64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 9d71e7cc..2923b799 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux -// +build mips,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index fd5ccd33..ce2750ee 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux -// +build mips64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 7704de77..3038811d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux -// +build mips64le,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index df00b875..efc6fed1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux -// +build mipsle,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index 0942840d..9a654b75 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux -// +build ppc,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 03487439..40d358e3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index bad06704..148c6ceb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 1b4c97c3..72ba8154 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux -// +build riscv64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index aa268d02..71e76550 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux -// +build s390x,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 444045b6..4abbdb9d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux -// +build sparc64,linux package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go index 9bc4c8f9..f22e7947 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go index bb05f655..066a7d83 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go index db40e3a1..439548ec 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go index 11121151..16085d3b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go index 26eba23b..afd13a3a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go index 5a547988..5d97f1f9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go index be58c4e1..34871cdc 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go index 52338266..5911bceb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && openbsd -// +build arm64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go index 605cfdb1..e4f24f3b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && openbsd -// +build mips64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go index d6724c01..ca50a793 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && openbsd -// +build ppc64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go index ddfd27a4..d7d7f790 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && openbsd -// +build riscv64,openbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go index 0400747c..14160576 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && solaris -// +build amd64,solaris package unix diff --git a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go index aec1efcb..54f31be6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build zos && s390x -// +build zos,s390x // Hand edited based on ztypes_linux_s390x.go // TODO: auto-generate. diff --git a/vendor/golang.org/x/sys/windows/aliases.go b/vendor/golang.org/x/sys/windows/aliases.go index a20ebea6..ce2d713d 100644 --- a/vendor/golang.org/x/sys/windows/aliases.go +++ b/vendor/golang.org/x/sys/windows/aliases.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && go1.9 -// +build windows,go1.9 package windows diff --git a/vendor/golang.org/x/sys/windows/empty.s b/vendor/golang.org/x/sys/windows/empty.s index fdbbbcd3..ba64caca 100644 --- a/vendor/golang.org/x/sys/windows/empty.s +++ b/vendor/golang.org/x/sys/windows/empty.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.12 -// +build !go1.12 // This file is here to allow bodyless functions with go:linkname for Go 1.11 // and earlier (see https://golang.org/issue/23311). diff --git a/vendor/golang.org/x/sys/windows/eventlog.go b/vendor/golang.org/x/sys/windows/eventlog.go index 2cd60645..6c366955 100644 --- a/vendor/golang.org/x/sys/windows/eventlog.go +++ b/vendor/golang.org/x/sys/windows/eventlog.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/mksyscall.go b/vendor/golang.org/x/sys/windows/mksyscall.go index 8563f79c..dbcdb090 100644 --- a/vendor/golang.org/x/sys/windows/mksyscall.go +++ b/vendor/golang.org/x/sys/windows/mksyscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build generate -// +build generate package windows diff --git a/vendor/golang.org/x/sys/windows/race.go b/vendor/golang.org/x/sys/windows/race.go index 9196b089..0f1bdc38 100644 --- a/vendor/golang.org/x/sys/windows/race.go +++ b/vendor/golang.org/x/sys/windows/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && race -// +build windows,race package windows diff --git a/vendor/golang.org/x/sys/windows/race0.go b/vendor/golang.org/x/sys/windows/race0.go index 7bae4817..0c78da78 100644 --- a/vendor/golang.org/x/sys/windows/race0.go +++ b/vendor/golang.org/x/sys/windows/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && !race -// +build windows,!race package windows diff --git a/vendor/golang.org/x/sys/windows/registry/key.go b/vendor/golang.org/x/sys/windows/registry/key.go index 6c8d97b6..fd863244 100644 --- a/vendor/golang.org/x/sys/windows/registry/key.go +++ b/vendor/golang.org/x/sys/windows/registry/key.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows // Package registry provides access to the Windows registry. // diff --git a/vendor/golang.org/x/sys/windows/registry/mksyscall.go b/vendor/golang.org/x/sys/windows/registry/mksyscall.go index ee74927d..bbf86ccf 100644 --- a/vendor/golang.org/x/sys/windows/registry/mksyscall.go +++ b/vendor/golang.org/x/sys/windows/registry/mksyscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build generate -// +build generate package registry diff --git a/vendor/golang.org/x/sys/windows/registry/syscall.go b/vendor/golang.org/x/sys/windows/registry/syscall.go index 41733512..f533091c 100644 --- a/vendor/golang.org/x/sys/windows/registry/syscall.go +++ b/vendor/golang.org/x/sys/windows/registry/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package registry diff --git a/vendor/golang.org/x/sys/windows/registry/value.go b/vendor/golang.org/x/sys/windows/registry/value.go index 2789f6f1..74db26b9 100644 --- a/vendor/golang.org/x/sys/windows/registry/value.go +++ b/vendor/golang.org/x/sys/windows/registry/value.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package registry diff --git a/vendor/golang.org/x/sys/windows/service.go b/vendor/golang.org/x/sys/windows/service.go index c44a1b96..a9dc6308 100644 --- a/vendor/golang.org/x/sys/windows/service.go +++ b/vendor/golang.org/x/sys/windows/service.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/str.go b/vendor/golang.org/x/sys/windows/str.go index 4fc01434..6a4f9ce6 100644 --- a/vendor/golang.org/x/sys/windows/str.go +++ b/vendor/golang.org/x/sys/windows/str.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package windows diff --git a/vendor/golang.org/x/sys/windows/syscall.go b/vendor/golang.org/x/sys/windows/syscall.go index 8732cdb9..e85ed6b9 100644 --- a/vendor/golang.org/x/sys/windows/syscall.go +++ b/vendor/golang.org/x/sys/windows/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows // Package windows contains an interface to the low-level operating system // primitives. OS details vary depending on the underlying system, and diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 35cfc57c..fb6cfd04 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -233,6 +233,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock //sys getTickCount64() (ms uint64) = kernel32.GetTickCount64 +//sys GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) //sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) //sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW //sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW @@ -969,7 +970,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) { if n > 0 { sl += int32(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index b88dc7c8..359780f6 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -1094,7 +1094,33 @@ const ( SOMAXCONN = 0x7fffffff - TCP_NODELAY = 1 + TCP_NODELAY = 1 + TCP_EXPEDITED_1122 = 2 + TCP_KEEPALIVE = 3 + TCP_MAXSEG = 4 + TCP_MAXRT = 5 + TCP_STDURG = 6 + TCP_NOURG = 7 + TCP_ATMARK = 8 + TCP_NOSYNRETRIES = 9 + TCP_TIMESTAMPS = 10 + TCP_OFFLOAD_PREFERENCE = 11 + TCP_CONGESTION_ALGORITHM = 12 + TCP_DELAY_FIN_ACK = 13 + TCP_MAXRTMS = 14 + TCP_FASTOPEN = 15 + TCP_KEEPCNT = 16 + TCP_KEEPIDLE = TCP_KEEPALIVE + TCP_KEEPINTVL = 17 + TCP_FAIL_CONNECT_ON_ICMP_ERROR = 18 + TCP_ICMP_ERROR_INFO = 19 + + UDP_NOCHECKSUM = 1 + UDP_SEND_MSG_SIZE = 2 + UDP_RECV_MAX_COALESCED_SIZE = 3 + UDP_CHECKSUM_COVERAGE = 20 + + UDP_COALESCED_INFO = 3 SHUT_RD = 0 SHUT_WR = 1 diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 8b1688de..db6282e0 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -253,6 +253,7 @@ var ( procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW") procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle") procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procGetFileTime = modkernel32.NewProc("GetFileTime") procGetFileType = modkernel32.NewProc("GetFileType") procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") @@ -2185,6 +2186,14 @@ func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte, return } +func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { + r1, _, e1 := syscall.Syscall6(procGetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetFileType(filehandle Handle) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0) n = uint32(r0) diff --git a/vendor/golang.org/x/term/term_unix.go b/vendor/golang.org/x/term/term_unix.go index 62c2b3f4..1ad0ddfe 100644 --- a/vendor/golang.org/x/term/term_unix.go +++ b/vendor/golang.org/x/term/term_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package term diff --git a/vendor/golang.org/x/term/term_unix_bsd.go b/vendor/golang.org/x/term/term_unix_bsd.go index 853b3d69..9dbf5462 100644 --- a/vendor/golang.org/x/term/term_unix_bsd.go +++ b/vendor/golang.org/x/term/term_unix_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package term diff --git a/vendor/golang.org/x/term/term_unix_other.go b/vendor/golang.org/x/term/term_unix_other.go index 1e8955c9..1b36de79 100644 --- a/vendor/golang.org/x/term/term_unix_other.go +++ b/vendor/golang.org/x/term/term_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || solaris || zos -// +build aix linux solaris zos package term diff --git a/vendor/golang.org/x/term/term_unsupported.go b/vendor/golang.org/x/term/term_unsupported.go index f1df8506..3c409e58 100644 --- a/vendor/golang.org/x/term/term_unsupported.go +++ b/vendor/golang.org/x/term/term_unsupported.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9 -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9 package term diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go index 8a7392c4..784bb880 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 package bidirule diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go index bb0a9200..8e1e9439 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 package bidirule diff --git a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go index 42fa8d72..d2bd7118 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go index 56a0e1ea..f76bdca2 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go index baacf32b..3aa2c3bd 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go index ffadb7be..a7137579 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go index 92cce580..f15746f7 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go index f517fdb2..c164d379 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package bidi diff --git a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go index f5a07882..1af161c7 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go index cb7239c4..eb73ecc3 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go index 11b27330..276cb8d8 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go index f65785e8..0cceffd7 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go index e1858b87..b0819e42 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go index 0175eae5..bf65457d 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package norm diff --git a/vendor/golang.org/x/text/width/tables10.0.0.go b/vendor/golang.org/x/text/width/tables10.0.0.go index cd9d91ca..07c1cb17 100644 --- a/vendor/golang.org/x/text/width/tables10.0.0.go +++ b/vendor/golang.org/x/text/width/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package width diff --git a/vendor/golang.org/x/text/width/tables11.0.0.go b/vendor/golang.org/x/text/width/tables11.0.0.go index 327eaef9..89288b3d 100644 --- a/vendor/golang.org/x/text/width/tables11.0.0.go +++ b/vendor/golang.org/x/text/width/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package width diff --git a/vendor/golang.org/x/text/width/tables12.0.0.go b/vendor/golang.org/x/text/width/tables12.0.0.go index 5c14ade6..755ee912 100644 --- a/vendor/golang.org/x/text/width/tables12.0.0.go +++ b/vendor/golang.org/x/text/width/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package width diff --git a/vendor/golang.org/x/text/width/tables13.0.0.go b/vendor/golang.org/x/text/width/tables13.0.0.go index b1fcb522..40c169ed 100644 --- a/vendor/golang.org/x/text/width/tables13.0.0.go +++ b/vendor/golang.org/x/text/width/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package width diff --git a/vendor/golang.org/x/text/width/tables15.0.0.go b/vendor/golang.org/x/text/width/tables15.0.0.go index 4b91e338..2b852896 100644 --- a/vendor/golang.org/x/text/width/tables15.0.0.go +++ b/vendor/golang.org/x/text/width/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package width diff --git a/vendor/golang.org/x/text/width/tables9.0.0.go b/vendor/golang.org/x/text/width/tables9.0.0.go index 6781f3d9..d981330a 100644 --- a/vendor/golang.org/x/text/width/tables9.0.0.go +++ b/vendor/golang.org/x/text/width/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package width diff --git a/vendor/golang.org/x/tools/cmd/stringer/stringer.go b/vendor/golang.org/x/tools/cmd/stringer/stringer.go index 998d1a51..2b19c93e 100644 --- a/vendor/golang.org/x/tools/cmd/stringer/stringer.go +++ b/vendor/golang.org/x/tools/cmd/stringer/stringer.go @@ -188,6 +188,8 @@ type Generator struct { trimPrefix string lineComment bool + + logf func(format string, args ...interface{}) // test logging hook; nil when not testing } func (g *Generator) Printf(format string, args ...interface{}) { @@ -221,13 +223,14 @@ func (g *Generator) parsePackage(patterns []string, tags []string) { // in a separate pass? For later. Tests: false, BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, + Logf: g.logf, } pkgs, err := packages.Load(cfg, patterns...) if err != nil { log.Fatal(err) } if len(pkgs) != 1 { - log.Fatalf("error: %d packages found", len(pkgs)) + log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " ")) } g.addPackage(pkgs[0]) } diff --git a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go index 3fbfebf3..1fc1de0b 100644 --- a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go +++ b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go @@ -64,8 +64,9 @@ type event struct { // depth-first order. It calls f(n) for each node n before it visits // n's children. // +// The complete traversal sequence is determined by ast.Inspect. // The types argument, if non-empty, enables type-based filtering of -// events. The function f if is called only for nodes whose type +// events. The function f is called only for nodes whose type // matches an element of the types slice. func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // Because it avoids postorder calls to f, and the pruning @@ -97,6 +98,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // of the non-nil children of the node, followed by a call of // f(n, false). // +// The complete traversal sequence is determined by ast.Inspect. // The types argument, if non-empty, enables type-based filtering of // events. The function f if is called only for nodes whose type // matches an element of the types slice. diff --git a/vendor/golang.org/x/tools/go/buildutil/fakecontext.go b/vendor/golang.org/x/tools/go/buildutil/fakecontext.go index 15025f64..763d1880 100644 --- a/vendor/golang.org/x/tools/go/buildutil/fakecontext.go +++ b/vendor/golang.org/x/tools/go/buildutil/fakecontext.go @@ -8,7 +8,6 @@ import ( "fmt" "go/build" "io" - "io/ioutil" "os" "path" "path/filepath" @@ -76,7 +75,7 @@ func FakeContext(pkgs map[string]map[string]string) *build.Context { if !ok { return nil, fmt.Errorf("file not found: %s", filename) } - return ioutil.NopCloser(strings.NewReader(content)), nil + return io.NopCloser(strings.NewReader(content)), nil } ctxt.IsAbsPath = func(path string) bool { path = filepath.ToSlash(path) diff --git a/vendor/golang.org/x/tools/go/buildutil/overlay.go b/vendor/golang.org/x/tools/go/buildutil/overlay.go index bdbfd931..7e371658 100644 --- a/vendor/golang.org/x/tools/go/buildutil/overlay.go +++ b/vendor/golang.org/x/tools/go/buildutil/overlay.go @@ -10,7 +10,6 @@ import ( "fmt" "go/build" "io" - "io/ioutil" "path/filepath" "strconv" "strings" @@ -33,7 +32,7 @@ func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Conte // TODO(dominikh): Implement IsDir, HasSubdir and ReadDir rc := func(data []byte) (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewBuffer(data)), nil + return io.NopCloser(bytes.NewBuffer(data)), nil } copy := *orig // make a copy diff --git a/vendor/golang.org/x/tools/go/internal/cgo/cgo.go b/vendor/golang.org/x/tools/go/internal/cgo/cgo.go index 3fce4800..38d5c6c7 100644 --- a/vendor/golang.org/x/tools/go/internal/cgo/cgo.go +++ b/vendor/golang.org/x/tools/go/internal/cgo/cgo.go @@ -57,7 +57,6 @@ import ( "go/build" "go/parser" "go/token" - "io/ioutil" "log" "os" "path/filepath" @@ -70,7 +69,7 @@ import ( // ProcessFiles invokes the cgo preprocessor on bp.CgoFiles, parses // the output and returns the resulting ASTs. func ProcessFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) { - tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C") + tmpdir, err := os.MkdirTemp("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C") if err != nil { return nil, err } diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go index 18a002f8..333676b7 100644 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -8,42 +8,46 @@ package packagesdriver import ( "context" "fmt" - "go/types" "strings" "golang.org/x/tools/internal/gocommand" ) -var debug = false - -func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) { +func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) { inv.Verb = "list" inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) var goarch, compiler string if rawErr != nil { - if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { - // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. + rawErrMsg := rawErr.Error() + if strings.Contains(rawErrMsg, "cannot find main module") || + strings.Contains(rawErrMsg, "go.mod file not found") { + // User's running outside of a module. + // All bets are off. Get GOARCH and guess compiler is gc. // TODO(matloob): Is this a problem in practice? inv.Verb = "env" inv.Args = []string{"GOARCH"} envout, enverr := gocmdRunner.Run(ctx, inv) if enverr != nil { - return nil, enverr + return "", "", enverr } goarch = strings.TrimSpace(envout.String()) compiler = "gc" + } else if friendlyErr != nil { + return "", "", friendlyErr } else { - return nil, friendlyErr + // This should be unreachable, but be defensive + // in case RunRaw's error results are inconsistent. + return "", "", rawErr } } else { fields := strings.Fields(stdout.String()) if len(fields) < 2 { - return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", + return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", stdout.String(), stderr.String()) } goarch = fields[0] compiler = fields[1] } - return types.SizesFor(compiler, goarch), nil + return compiler, goarch, nil } diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index da4ab89f..a7a8f73e 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -35,7 +35,7 @@ The Package struct provides basic information about the package, including - Imports, a map from source import strings to the Packages they name; - Types, the type information for the package's exported symbols; - Syntax, the parsed syntax trees for the package's source code; and - - TypeInfo, the result of a complete type-check of the package syntax trees. + - TypesInfo, the result of a complete type-check of the package syntax trees. (See the documentation for type Package for the complete list of fields and more detailed descriptions.) diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index e84f19df..c1292b30 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -9,8 +9,6 @@ import ( "context" "encoding/json" "fmt" - "go/types" - "io/ioutil" "log" "os" "path" @@ -153,10 +151,10 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { sizeswg.Add(1) go func() { - var sizes types.Sizes - sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) - // types.SizesFor always returns nil or a *types.StdSizes. - response.dr.Sizes, _ = sizes.(*types.StdSizes) + compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) + sizeserr = err + response.dr.Compiler = compiler + response.dr.Arch = arch sizeswg.Done() }() } @@ -210,62 +208,6 @@ extractQueries: } } - // Only use go/packages' overlay processing if we're using a Go version - // below 1.16. Otherwise, go list handles it. - if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 { - modifiedPkgs, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return nil, err - } - - var containsCandidates []string - if len(containFiles) > 0 { - containsCandidates = append(containsCandidates, modifiedPkgs...) - containsCandidates = append(containsCandidates, needPkgs...) - } - if err := state.addNeededOverlayPackages(response, needPkgs); err != nil { - return nil, err - } - // Check candidate packages for containFiles. - if len(containFiles) > 0 { - for _, id := range containsCandidates { - pkg, ok := response.seenPackages[id] - if !ok { - response.addPackage(&Package{ - ID: id, - Errors: []Error{{ - Kind: ListError, - Msg: fmt.Sprintf("package %s expected but not seen", id), - }}, - }) - continue - } - for _, f := range containFiles { - for _, g := range pkg.GoFiles { - if sameFile(f, g) { - response.addRoot(id) - } - } - } - } - } - // Add root for any package that matches a pattern. This applies only to - // packages that are modified by overlays, since they are not added as - // roots automatically. - for _, pattern := range restPatterns { - match := matchPattern(pattern) - for _, pkgID := range modifiedPkgs { - pkg, ok := response.seenPackages[pkgID] - if !ok { - continue - } - if match(pkg.PkgPath) { - response.addRoot(pkg.ID) - } - } - } - } - sizeswg.Wait() if sizeserr != nil { return nil, sizeserr @@ -273,24 +215,6 @@ extractQueries: return response.dr, nil } -func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error { - if len(pkgs) == 0 { - return nil - } - dr, err := state.createDriverResponse(pkgs...) - if err != nil { - return err - } - for _, pkg := range dr.Packages { - response.addPackage(pkg) - } - _, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return err - } - return state.addNeededOverlayPackages(response, needPkgs) -} - func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. @@ -671,6 +595,9 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse // Temporary work-around for golang/go#39986. Parse filenames out of // error messages. This happens if there are unrecoverable syntax // errors in the source, so we can't match on a specific error message. + // + // TODO(rfindley): remove this heuristic, in favor of considering + // InvalidGoFiles from the list driver. if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { addFilenameFromPos := func(pos string) bool { split := strings.Split(pos, ":") @@ -1107,7 +1034,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err if len(state.cfg.Overlay) == 0 { return "", func() {}, nil } - dir, err := ioutil.TempDir("", "gopackages-*") + dir, err := os.MkdirTemp("", "gopackages-*") if err != nil { return "", nil, err } @@ -1126,7 +1053,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err // Create a unique filename for the overlaid files, to avoid // creating nested directories. noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") - f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) + f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator)) if err != nil { return "", func() {}, err } @@ -1144,7 +1071,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err } // Write out the overlay file that contains the filepath mappings. filename = filepath.Join(dir, "overlay.json") - if err := ioutil.WriteFile(filename, b, 0665); err != nil { + if err := os.WriteFile(filename, b, 0665); err != nil { return "", func() {}, err } return filename, cleanup, nil diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index 9576b472..d823c474 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -6,314 +6,11 @@ package packages import ( "encoding/json" - "fmt" - "go/parser" - "go/token" - "os" "path/filepath" - "regexp" - "sort" - "strconv" - "strings" "golang.org/x/tools/internal/gocommand" ) -// processGolistOverlay provides rudimentary support for adding -// files that don't exist on disk to an overlay. The results can be -// sometimes incorrect. -// TODO(matloob): Handle unsupported cases, including the following: -// - determining the correct package to add given a new import path -func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { - havePkgs := make(map[string]string) // importPath -> non-test package ID - needPkgsSet := make(map[string]bool) - modifiedPkgsSet := make(map[string]bool) - - pkgOfDir := make(map[string][]*Package) - for _, pkg := range response.dr.Packages { - // This is an approximation of import path to id. This can be - // wrong for tests, vendored packages, and a number of other cases. - havePkgs[pkg.PkgPath] = pkg.ID - dir, err := commonDir(pkg.GoFiles) - if err != nil { - return nil, nil, err - } - if dir != "" { - pkgOfDir[dir] = append(pkgOfDir[dir], pkg) - } - } - - // If no new imports are added, it is safe to avoid loading any needPkgs. - // Otherwise, it's hard to tell which package is actually being loaded - // (due to vendoring) and whether any modified package will show up - // in the transitive set of dependencies (because new imports are added, - // potentially modifying the transitive set of dependencies). - var overlayAddsImports bool - - // If both a package and its test package are created by the overlay, we - // need the real package first. Process all non-test files before test - // files, and make the whole process deterministic while we're at it. - var overlayFiles []string - for opath := range state.cfg.Overlay { - overlayFiles = append(overlayFiles, opath) - } - sort.Slice(overlayFiles, func(i, j int) bool { - iTest := strings.HasSuffix(overlayFiles[i], "_test.go") - jTest := strings.HasSuffix(overlayFiles[j], "_test.go") - if iTest != jTest { - return !iTest // non-tests are before tests. - } - return overlayFiles[i] < overlayFiles[j] - }) - for _, opath := range overlayFiles { - contents := state.cfg.Overlay[opath] - base := filepath.Base(opath) - dir := filepath.Dir(opath) - var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant - var testVariantOf *Package // if opath is a test file, this is the package it is testing - var fileExists bool - isTestFile := strings.HasSuffix(opath, "_test.go") - pkgName, ok := extractPackageName(opath, contents) - if !ok { - // Don't bother adding a file that doesn't even have a parsable package statement - // to the overlay. - continue - } - // If all the overlay files belong to a different package, change the - // package name to that package. - maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) - nextPackage: - for _, p := range response.dr.Packages { - if pkgName != p.Name && p.ID != "command-line-arguments" { - continue - } - for _, f := range p.GoFiles { - if !sameFile(filepath.Dir(f), dir) { - continue - } - // Make sure to capture information on the package's test variant, if needed. - if isTestFile && !hasTestFiles(p) { - // TODO(matloob): Are there packages other than the 'production' variant - // of a package that this can match? This shouldn't match the test main package - // because the file is generated in another directory. - testVariantOf = p - continue nextPackage - } else if !isTestFile && hasTestFiles(p) { - // We're examining a test variant, but the overlaid file is - // a non-test file. Because the overlay implementation - // (currently) only adds a file to one package, skip this - // package, so that we can add the file to the production - // variant of the package. (https://golang.org/issue/36857 - // tracks handling overlays on both the production and test - // variant of a package). - continue nextPackage - } - if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { - // We have already seen the production version of the - // for which p is a test variant. - if hasTestFiles(p) { - testVariantOf = pkg - } - } - pkg = p - if filepath.Base(f) == base { - fileExists = true - } - } - } - // The overlay could have included an entirely new package or an - // ad-hoc package. An ad-hoc package is one that we have manually - // constructed from inadequate `go list` results for a file= query. - // It will have the ID command-line-arguments. - if pkg == nil || pkg.ID == "command-line-arguments" { - // Try to find the module or gopath dir the file is contained in. - // Then for modules, add the module opath to the beginning. - pkgPath, ok, err := state.getPkgPath(dir) - if err != nil { - return nil, nil, err - } - if !ok { - break - } - var forTest string // only set for x tests - isXTest := strings.HasSuffix(pkgName, "_test") - if isXTest { - forTest = pkgPath - pkgPath += "_test" - } - id := pkgPath - if isTestFile { - if isXTest { - id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) - } else { - id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) - } - } - if pkg != nil { - // TODO(rstambler): We should change the package's path and ID - // here. The only issue is that this messes with the roots. - } else { - // Try to reclaim a package with the same ID, if it exists in the response. - for _, p := range response.dr.Packages { - if reclaimPackage(p, id, opath, contents) { - pkg = p - break - } - } - // Otherwise, create a new package. - if pkg == nil { - pkg = &Package{ - PkgPath: pkgPath, - ID: id, - Name: pkgName, - Imports: make(map[string]*Package), - } - response.addPackage(pkg) - havePkgs[pkg.PkgPath] = id - // Add the production package's sources for a test variant. - if isTestFile && !isXTest && testVariantOf != nil { - pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) - // Add the package under test and its imports to the test variant. - pkg.forTest = testVariantOf.PkgPath - for k, v := range testVariantOf.Imports { - pkg.Imports[k] = &Package{ID: v.ID} - } - } - if isXTest { - pkg.forTest = forTest - } - } - } - } - if !fileExists { - pkg.GoFiles = append(pkg.GoFiles, opath) - // TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior - // if the file will be ignored due to its build tags. - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) - modifiedPkgsSet[pkg.ID] = true - } - imports, err := extractImports(opath, contents) - if err != nil { - // Let the parser or type checker report errors later. - continue - } - for _, imp := range imports { - // TODO(rstambler): If the package is an x test and the import has - // a test variant, make sure to replace it. - if _, found := pkg.Imports[imp]; found { - continue - } - overlayAddsImports = true - id, ok := havePkgs[imp] - if !ok { - var err error - id, err = state.resolveImport(dir, imp) - if err != nil { - return nil, nil, err - } - } - pkg.Imports[imp] = &Package{ID: id} - // Add dependencies to the non-test variant version of this package as well. - if testVariantOf != nil { - testVariantOf.Imports[imp] = &Package{ID: id} - } - } - } - - // toPkgPath guesses the package path given the id. - toPkgPath := func(sourceDir, id string) (string, error) { - if i := strings.IndexByte(id, ' '); i >= 0 { - return state.resolveImport(sourceDir, id[:i]) - } - return state.resolveImport(sourceDir, id) - } - - // Now that new packages have been created, do another pass to determine - // the new set of missing packages. - for _, pkg := range response.dr.Packages { - for _, imp := range pkg.Imports { - if len(pkg.GoFiles) == 0 { - return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) - } - pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) - if err != nil { - return nil, nil, err - } - if _, ok := havePkgs[pkgPath]; !ok { - needPkgsSet[pkgPath] = true - } - } - } - - if overlayAddsImports { - needPkgs = make([]string, 0, len(needPkgsSet)) - for pkg := range needPkgsSet { - needPkgs = append(needPkgs, pkg) - } - } - modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) - for pkg := range modifiedPkgsSet { - modifiedPkgs = append(modifiedPkgs, pkg) - } - return modifiedPkgs, needPkgs, err -} - -// resolveImport finds the ID of a package given its import path. -// In particular, it will find the right vendored copy when in GOPATH mode. -func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { - env, err := state.getEnv() - if err != nil { - return "", err - } - if env["GOMOD"] != "" { - return importPath, nil - } - - searchDir := sourceDir - for { - vendorDir := filepath.Join(searchDir, "vendor") - exists, ok := state.vendorDirs[vendorDir] - if !ok { - info, err := os.Stat(vendorDir) - exists = err == nil && info.IsDir() - state.vendorDirs[vendorDir] = exists - } - - if exists { - vendoredPath := filepath.Join(vendorDir, importPath) - if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { - // We should probably check for .go files here, but shame on anyone who fools us. - path, ok, err := state.getPkgPath(vendoredPath) - if err != nil { - return "", err - } - if ok { - return path, nil - } - } - } - - // We know we've hit the top of the filesystem when we Dir / and get /, - // or C:\ and get C:\, etc. - next := filepath.Dir(searchDir) - if next == searchDir { - break - } - searchDir = next - } - return importPath, nil -} - -func hasTestFiles(p *Package) bool { - for _, f := range p.GoFiles { - if strings.HasSuffix(f, "_test.go") { - return true - } - } - return false -} - // determineRootDirs returns a mapping from absolute directories that could // contain code to their corresponding import path prefixes. func (state *golistState) determineRootDirs() (map[string]string, error) { @@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { } return m, nil } - -func extractImports(filename string, contents []byte) ([]string, error) { - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? - if err != nil { - return nil, err - } - var res []string - for _, imp := range f.Imports { - quotedPath := imp.Path.Value - path, err := strconv.Unquote(quotedPath) - if err != nil { - return nil, err - } - res = append(res, path) - } - return res, nil -} - -// reclaimPackage attempts to reuse a package that failed to load in an overlay. -// -// If the package has errors and has no Name, GoFiles, or Imports, -// then it's possible that it doesn't yet exist on disk. -func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - if pkg.ID != id { - return false - } - if len(pkg.Errors) != 1 { - return false - } - if pkg.Name != "" || pkg.ExportFile != "" { - return false - } - if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { - return false - } - if len(pkg.Imports) > 0 { - return false - } - pkgName, ok := extractPackageName(filename, contents) - if !ok { - return false - } - pkg.Name = pkgName - pkg.Errors = nil - return true -} - -func extractPackageName(filename string, contents []byte) (string, bool) { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset? - if err != nil { - return "", false - } - return f.Name.Name, true -} - -// commonDir returns the directory that all files are in, "" if files is empty, -// or an error if they aren't in the same directory. -func commonDir(files []string) (string, error) { - seen := make(map[string]bool) - for _, f := range files { - seen[filepath.Dir(f)] = true - } - if len(seen) > 1 { - return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen) - } - for k := range seen { - // seen has only one element; return it. - return k, nil - } - return "", nil // no files -} - -// It is possible that the files in the disk directory dir have a different package -// name from newName, which is deduced from the overlays. If they all have a different -// package name, and they all have the same package name, then that name becomes -// the package name. -// It returns true if it changes the package name, false otherwise. -func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { - names := make(map[string]int) - for _, p := range pkgsOfDir { - names[p.Name]++ - } - if len(names) != 1 { - // some files are in different packages - return - } - var oldName string - for k := range names { - oldName = k - } - if newName == oldName { - return - } - // We might have a case where all of the package names in the directory are - // the same, but the overlay file is for an x test, which belongs to its - // own package. If the x test does not yet exist on disk, we may not yet - // have its package name on disk, but we should not rename the packages. - // - // We use a heuristic to determine if this file belongs to an x test: - // The test file should have a package name whose package name has a _test - // suffix or looks like "newName_test". - maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") - if isTestFile && maybeXTest { - return - } - for _, p := range pkgsOfDir { - p.Name = newName - } -} - -// This function is copy-pasted from -// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360. -// It should be deleted when we remove support for overlays from go/packages. -// -// NOTE: This does not handle any ./... or ./ style queries, as this function -// doesn't know the working directory. -// -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Unfortunately, there are two special cases. Quoting "go help packages": -// -// First, /... at the end of the pattern can match an empty string, -// so that net/... matches both net and packages in its subdirectories, like net/http. -// Second, any slash-separated pattern element containing a wildcard never -// participates in a match of the "vendor" element in the path of a vendored -// package, so that ./... does not match packages in subdirectories of -// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. -// Note, however, that a directory named vendor that itself contains code -// is not a vendored package: cmd/vendor would be a command named vendor, -// and the pattern cmd/... matches it. -func matchPattern(pattern string) func(name string) bool { - // Convert pattern to regular expression. - // The strategy for the trailing /... is to nest it in an explicit ? expression. - // The strategy for the vendor exclusion is to change the unmatchable - // vendor strings to a disallowed code point (vendorChar) and to use - // "(anything but that codepoint)*" as the implementation of the ... wildcard. - // This is a bit complicated but the obvious alternative, - // namely a hand-written search like in most shell glob matchers, - // is too easy to make accidentally exponential. - // Using package regexp guarantees linear-time matching. - - const vendorChar = "\x00" - - if strings.Contains(pattern, vendorChar) { - return func(name string) bool { return false } - } - - re := regexp.QuoteMeta(pattern) - re = replaceVendor(re, vendorChar) - switch { - case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): - re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` - case re == vendorChar+`/\.\.\.`: - re = `(/vendor|/` + vendorChar + `/\.\.\.)` - case strings.HasSuffix(re, `/\.\.\.`): - re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` - } - re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) - - reg := regexp.MustCompile(`^` + re + `$`) - - return func(name string) bool { - if strings.Contains(name, vendorChar) { - return false - } - return reg.MatchString(replaceVendor(name, vendorChar)) - } -} - -// replaceVendor returns the result of replacing -// non-trailing vendor path elements in x with repl. -func replaceVendor(x, repl string) string { - if !strings.Contains(x, "vendor") { - return x - } - elem := strings.Split(x, "/") - for i := 0; i < len(elem)-1; i++ { - if elem[i] == "vendor" { - elem[i] = repl - } - } - return strings.Join(elem, "/") -} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 632be722..6cbd3de8 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -16,7 +16,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -220,8 +219,10 @@ type driverResponse struct { // lists of multiple drivers, go/packages will fall back to the next driver. NotHandled bool - // Sizes, if not nil, is the types.Sizes to use when type checking. - Sizes *types.StdSizes + // Compiler and Arch are the arguments pass of types.SizesFor + // to get a types.Sizes to use when type checking. + Compiler string + Arch string // Roots is the set of package IDs that make up the root packages. // We have to encode this separately because when we encode a single package @@ -257,31 +258,52 @@ type driverResponse struct { // proceeding with further analysis. The PrintErrors function is // provided for convenient display of all errors. func Load(cfg *Config, patterns ...string) ([]*Package, error) { - l := newLoader(cfg) - response, err := defaultDriver(&l.Config, patterns...) + ld := newLoader(cfg) + response, external, err := defaultDriver(&ld.Config, patterns...) if err != nil { return nil, err } - l.sizes = response.Sizes - return l.refine(response) + + ld.sizes = types.SizesFor(response.Compiler, response.Arch) + if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 { + // Type size information is needed but unavailable. + if external { + // An external driver may fail to populate the Compiler/GOARCH fields, + // especially since they are relatively new (see #63700). + // Provide a sensible fallback in this case. + ld.sizes = types.SizesFor("gc", runtime.GOARCH) + if ld.sizes == nil { // gccgo-only arch + ld.sizes = types.SizesFor("gc", "amd64") + } + } else { + // Go list should never fail to deliver accurate size information. + // Reject the whole Load since the error is the same for every package. + return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q", + response.Compiler, response.Arch) + } + } + + return ld.refine(response) } // defaultDriver is a driver that implements go/packages' fallback behavior. // It will try to request to an external driver, if one exists. If there's // no external driver, or the driver returns a response with NotHandled set, // defaultDriver will fall back to the go list driver. -func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { - driver := findExternalDriver(cfg) - if driver == nil { - driver = goListDriver - } - response, err := driver(cfg, patterns...) - if err != nil { - return response, err - } else if response.NotHandled { - return goListDriver(cfg, patterns...) +// The boolean result indicates that an external driver handled the request. +func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) { + if driver := findExternalDriver(cfg); driver != nil { + response, err := driver(cfg, patterns...) + if err != nil { + return nil, false, err + } else if !response.NotHandled { + return response, true, nil + } + // (fall through) } - return response, nil + + response, err := goListDriver(cfg, patterns...) + return response, false, err } // A Package describes a loaded Go package. @@ -552,7 +574,7 @@ type loaderPackage struct { type loader struct { pkgs map[string]*loaderPackage Config - sizes types.Sizes + sizes types.Sizes // non-nil if needed by mode parseCache map[string]*parseValue parseCacheMu sync.Mutex exportMu sync.Mutex // enforces mutual exclusion of exportdata operations @@ -630,7 +652,7 @@ func newLoader(cfg *Config) *loader { return ld } -// refine connects the supplied packages into a graph and then adds type and +// refine connects the supplied packages into a graph and then adds type // and syntax information as requested by the LoadMode. func (ld *loader) refine(response *driverResponse) ([]*Package, error) { roots := response.Roots @@ -677,39 +699,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } } - // Materialize the import graph. - - const ( - white = 0 // new - grey = 1 // in progress - black = 2 // complete - ) - - // visit traverses the import graph, depth-first, - // and materializes the graph as Packages.Imports. - // - // Valid imports are saved in the Packages.Import map. - // Invalid imports (cycles and missing nodes) are saved in the importErrors map. - // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. - // - // visit returns whether the package needs src or has a transitive - // dependency on a package that does. These are the only packages - // for which we load source code. - var stack []*loaderPackage - var visit func(lpkg *loaderPackage) bool - var srcPkgs []*loaderPackage - visit = func(lpkg *loaderPackage) bool { - switch lpkg.color { - case black: - return lpkg.needsrc - case grey: - panic("internal error: grey node") - } - lpkg.color = grey - stack = append(stack, lpkg) // push - stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports - // If NeedImports isn't set, the imports fields will all be zeroed out. - if ld.Mode&NeedImports != 0 { + if ld.Mode&NeedImports != 0 { + // Materialize the import graph. + + const ( + white = 0 // new + grey = 1 // in progress + black = 2 // complete + ) + + // visit traverses the import graph, depth-first, + // and materializes the graph as Packages.Imports. + // + // Valid imports are saved in the Packages.Import map. + // Invalid imports (cycles and missing nodes) are saved in the importErrors map. + // Thus, even in the presence of both kinds of errors, + // the Import graph remains a DAG. + // + // visit returns whether the package needs src or has a transitive + // dependency on a package that does. These are the only packages + // for which we load source code. + var stack []*loaderPackage + var visit func(lpkg *loaderPackage) bool + visit = func(lpkg *loaderPackage) bool { + switch lpkg.color { + case black: + return lpkg.needsrc + case grey: + panic("internal error: grey node") + } + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports lpkg.Imports = make(map[string]*Package, len(stubs)) for importPath, ipkg := range stubs { var importErr error @@ -733,40 +754,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } lpkg.Imports[importPath] = imp.Package } - } - if lpkg.needsrc { - srcPkgs = append(srcPkgs, lpkg) - } - if ld.Mode&NeedTypesSizes != 0 { - lpkg.TypesSizes = ld.sizes - } - stack = stack[:len(stack)-1] // pop - lpkg.color = black - return lpkg.needsrc - } + // Complete type information is required for the + // immediate dependencies of each source package. + if lpkg.needsrc && ld.Mode&NeedTypes != 0 { + for _, ipkg := range lpkg.Imports { + ld.pkgs[ipkg.ID].needtypes = true + } + } - if ld.Mode&NeedImports == 0 { - // We do this to drop the stub import packages that we are not even going to try to resolve. - for _, lpkg := range initial { - lpkg.Imports = nil + // NeedTypeSizes causes TypeSizes to be set even + // on packages for which types aren't needed. + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes + } + stack = stack[:len(stack)-1] // pop + lpkg.color = black + + return lpkg.needsrc } - } else { + // For each initial package, create its import DAG. for _, lpkg := range initial { visit(lpkg) } - } - if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { - for _, lpkg := range srcPkgs { - // Complete type information is required for the - // immediate dependencies of each source package. - for _, ipkg := range lpkg.Imports { - imp := ld.pkgs[ipkg.ID] - imp.needtypes = true - } + + } else { + // !NeedImports: drop the stub (ID-only) import packages + // that we are not even going to try to resolve. + for _, lpkg := range initial { + lpkg.Imports = nil } } + // Load type data and syntax if needed, starting at // the initial packages (roots of the import DAG). if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { @@ -1041,7 +1061,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, Error: appendError, - Sizes: ld.sizes, + Sizes: ld.sizes, // may be nil + } + if lpkg.Module != nil && lpkg.Module.GoVersion != "" { + typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion) } if (ld.Mode & typecheckCgo) != 0 { if !typesinternal.SetUsesCgo(tc) { @@ -1122,7 +1145,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { var err error if src == nil { ioLimit <- true // wait - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) <-ioLimit // signal } if err != nil { diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go new file mode 100644 index 00000000..e742ecc4 --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -0,0 +1,752 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package objectpath defines a naming scheme for types.Objects +// (that is, named entities in Go programs) relative to their enclosing +// package. +// +// Type-checker objects are canonical, so they are usually identified by +// their address in memory (a pointer), but a pointer has meaning only +// within one address space. By contrast, objectpath names allow the +// identity of an object to be sent from one program to another, +// establishing a correspondence between types.Object variables that are +// distinct but logically equivalent. +// +// A single object may have multiple paths. In this example, +// +// type A struct{ X int } +// type B A +// +// the field X has two paths due to its membership of both A and B. +// The For(obj) function always returns one of these paths, arbitrarily +// but consistently. +package objectpath + +import ( + "fmt" + "go/types" + "strconv" + "strings" + + "golang.org/x/tools/internal/typeparams" +) + +// A Path is an opaque name that identifies a types.Object +// relative to its package. Conceptually, the name consists of a +// sequence of destructuring operations applied to the package scope +// to obtain the original object. +// The name does not include the package itself. +type Path string + +// Encoding +// +// An object path is a textual and (with training) human-readable encoding +// of a sequence of destructuring operators, starting from a types.Package. +// The sequences represent a path through the package/object/type graph. +// We classify these operators by their type: +// +// PO package->object Package.Scope.Lookup +// OT object->type Object.Type +// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] +// TO type->object Type.{At,Field,Method,Obj} [AFMO] +// +// All valid paths start with a package and end at an object +// and thus may be defined by the regular language: +// +// objectpath = PO (OT TT* TO)* +// +// The concrete encoding follows directly: +// - The only PO operator is Package.Scope.Lookup, which requires an identifier. +// - The only OT operator is Object.Type, +// which we encode as '.' because dot cannot appear in an identifier. +// - The TT operators are encoded as [EKPRUTC]; +// one of these (TypeParam) requires an integer operand, +// which is encoded as a string of decimal digits. +// - The TO operators are encoded as [AFMO]; +// three of these (At,Field,Method) require an integer operand, +// which is encoded as a string of decimal digits. +// These indices are stable across different representations +// of the same package, even source and export data. +// The indices used are implementation specific and may not correspond to +// the argument to the go/types function. +// +// In the example below, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// field X has the path "T.UM0.RA1.F0", +// representing the following sequence of operations: +// +// p.Lookup("T") T +// .Type().Underlying().Method(0). f +// .Type().Results().At(1) b +// .Type().Field(0) X +// +// The encoding is not maximally compact---every R or P is +// followed by an A, for example---but this simplifies the +// encoder and decoder. +const ( + // object->type operators + opType = '.' // .Type() (Object) + + // type->type operators + opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) + opKey = 'K' // .Key() (Map) + opParams = 'P' // .Params() (Signature) + opResults = 'R' // .Results() (Signature) + opUnderlying = 'U' // .Underlying() (Named) + opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) + opConstraint = 'C' // .Constraint() (TypeParam) + + // type->object operators + opAt = 'A' // .At(i) (Tuple) + opField = 'F' // .Field(i) (Struct) + opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) + opObj = 'O' // .Obj() (Named, TypeParam) +) + +// For is equivalent to new(Encoder).For(obj). +// +// It may be more efficient to reuse a single Encoder across several calls. +func For(obj types.Object) (Path, error) { + return new(Encoder).For(obj) +} + +// An Encoder amortizes the cost of encoding the paths of multiple objects. +// The zero value of an Encoder is ready to use. +type Encoder struct { + scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects +} + +// For returns the path to an object relative to its package, +// or an error if the object is not accessible from the package's Scope. +// +// The For function guarantees to return a path only for the following objects: +// - package-level types +// - exported package-level non-types +// - methods +// - parameter and result variables +// - struct fields +// These objects are sufficient to define the API of their package. +// The objects described by a package's export data are drawn from this set. +// +// The set of objects accessible from a package's Scope depends on +// whether the package was produced by type-checking syntax, or +// reading export data; the latter may have a smaller Scope since +// export data trims objects that are not reachable from an exported +// declaration. For example, the For function will return a path for +// an exported method of an unexported type that is not reachable +// from any public declaration; this path will cause the Object +// function to fail if called on a package loaded from export data. +// TODO(adonovan): is this a bug or feature? Should this package +// compute accessibility in the same way? +// +// For does not return a path for predeclared names, imported package +// names, local names, and unexported package-level names (except +// types). +// +// Example: given this definition, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// For(X) would return a path that denotes the following sequence of operations: +// +// p.Scope().Lookup("T") (TypeName T) +// .Type().Underlying().Method(0). (method Func f) +// .Type().Results().At(1) (field Var b) +// .Type().Field(0) (field Var X) +// +// where p is the package (*types.Package) to which X belongs. +func (enc *Encoder) For(obj types.Object) (Path, error) { + pkg := obj.Pkg() + + // This table lists the cases of interest. + // + // Object Action + // ------ ------ + // nil reject + // builtin reject + // pkgname reject + // label reject + // var + // package-level accept + // func param/result accept + // local reject + // struct field accept + // const + // package-level accept + // local reject + // func + // package-level accept + // init functions reject + // concrete method accept + // interface method accept + // type + // package-level accept + // local reject + // + // The only accessible package-level objects are members of pkg itself. + // + // The cases are handled in four steps: + // + // 1. reject nil and builtin + // 2. accept package-level objects + // 3. reject obviously invalid objects + // 4. search the API for the path to the param/result/field/method. + + // 1. reference to nil or builtin? + if pkg == nil { + return "", fmt.Errorf("predeclared %s has no path", obj) + } + scope := pkg.Scope() + + // 2. package-level object? + if scope.Lookup(obj.Name()) == obj { + // Only exported objects (and non-exported types) have a path. + // Non-exported types may be referenced by other objects. + if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { + return "", fmt.Errorf("no path for non-exported %v", obj) + } + return Path(obj.Name()), nil + } + + // 3. Not a package-level object. + // Reject obviously non-viable cases. + switch obj := obj.(type) { + case *types.TypeName: + if _, ok := obj.Type().(*typeparams.TypeParam); !ok { + // With the exception of type parameters, only package-level type names + // have a path. + return "", fmt.Errorf("no path for %v", obj) + } + case *types.Const, // Only package-level constants have a path. + *types.Label, // Labels are function-local. + *types.PkgName: // PkgNames are file-local. + return "", fmt.Errorf("no path for %v", obj) + + case *types.Var: + // Could be: + // - a field (obj.IsField()) + // - a func parameter or result + // - a local var. + // Sadly there is no way to distinguish + // a param/result from a local + // so we must proceed to the find. + + case *types.Func: + // A func, if not package-level, must be a method. + if recv := obj.Type().(*types.Signature).Recv(); recv == nil { + return "", fmt.Errorf("func is not a method: %v", obj) + } + + if path, ok := enc.concreteMethod(obj); ok { + // Fast path for concrete methods that avoids looping over scope. + return path, nil + } + + default: + panic(obj) + } + + // 4. Search the API for the path to the var (field/param/result) or method. + + // First inspect package-level named types. + // In the presence of path aliases, these give + // the best paths because non-types may + // refer to types, but not the reverse. + empty := make([]byte, 0, 48) // initial space + objs := enc.scopeObjects(scope) + for _, o := range objs { + tname, ok := o.(*types.TypeName) + if !ok { + continue // handle non-types in second pass + } + + path := append(empty, o.Name()...) + path = append(path, opType) + + T := o.Type() + + if tname.IsAlias() { + // type alias + if r := find(obj, T, path, nil); r != nil { + return Path(r), nil + } + } else { + if named, _ := T.(*types.Named); named != nil { + if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil { + // generic named type + return Path(r), nil + } + } + // defined (named) type + if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil { + return Path(r), nil + } + } + } + + // Then inspect everything else: + // non-types, and declared methods of defined types. + for _, o := range objs { + path := append(empty, o.Name()...) + if _, ok := o.(*types.TypeName); !ok { + if o.Exported() { + // exported non-type (const, var, func) + if r := find(obj, o.Type(), append(path, opType), nil); r != nil { + return Path(r), nil + } + } + continue + } + + // Inspect declared methods of defined types. + if T, ok := o.Type().(*types.Named); ok { + path = append(path, opType) + // The method index here is always with respect + // to the underlying go/types data structures, + // which ultimately derives from source order + // and must be preserved by export data. + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return Path(path2), nil // found declared method + } + if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { + return Path(r), nil + } + } + } + } + + return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) +} + +func appendOpArg(path []byte, op byte, arg int) []byte { + path = append(path, op) + path = strconv.AppendInt(path, int64(arg), 10) + return path +} + +// concreteMethod returns the path for meth, which must have a non-nil receiver. +// The second return value indicates success and may be false if the method is +// an interface method or if it is an instantiated method. +// +// This function is just an optimization that avoids the general scope walking +// approach. You are expected to fall back to the general approach if this +// function fails. +func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { + // Concrete methods can only be declared on package-scoped named types. For + // that reason we can skip the expensive walk over the package scope: the + // path will always be package -> named type -> method. We can trivially get + // the type name from the receiver, and only have to look over the type's + // methods to find the method index. + // + // Methods on generic types require special consideration, however. Consider + // the following package: + // + // L1: type S[T any] struct{} + // L2: func (recv S[A]) Foo() { recv.Bar() } + // L3: func (recv S[B]) Bar() { } + // L4: type Alias = S[int] + // L5: func _[T any]() { var s S[int]; s.Foo() } + // + // The receivers of methods on generic types are instantiations. L2 and L3 + // instantiate S with the type-parameters A and B, which are scoped to the + // respective methods. L4 and L5 each instantiate S with int. Each of these + // instantiations has its own method set, full of methods (and thus objects) + // with receivers whose types are the respective instantiations. In other + // words, we have + // + // S[A].Foo, S[A].Bar + // S[B].Foo, S[B].Bar + // S[int].Foo, S[int].Bar + // + // We may thus be trying to produce object paths for any of these objects. + // + // S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo + // and S.Bar, which are the paths that this function naturally produces. + // + // S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that + // don't correspond to the origin methods. For S[int], this is significant. + // The most precise object path for S[int].Foo, for example, is Alias.Foo, + // not S.Foo. Our function, however, would produce S.Foo, which would + // resolve to a different object. + // + // For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are + // still the correct paths, since only the origin methods have meaningful + // paths. But this is likely only true for trivial cases and has edge cases. + // Since this function is only an optimization, we err on the side of giving + // up, deferring to the slower but definitely correct algorithm. Most users + // of objectpath will only be giving us origin methods, anyway, as referring + // to instantiated methods is usually not useful. + + if typeparams.OriginMethod(meth) != meth { + return "", false + } + + recvT := meth.Type().(*types.Signature).Recv().Type() + if ptr, ok := recvT.(*types.Pointer); ok { + recvT = ptr.Elem() + } + + named, ok := recvT.(*types.Named) + if !ok { + return "", false + } + + if types.IsInterface(named) { + // Named interfaces don't have to be package-scoped + // + // TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface + // methods, too, I think. + return "", false + } + + // Preallocate space for the name, opType, opMethod, and some digits. + name := named.Obj().Name() + path := make([]byte, 0, len(name)+8) + path = append(path, name...) + path = append(path, opType) + + // Method indices are w.r.t. the go/types data structures, + // ultimately deriving from source order, + // which is preserved by export data. + for i := 0; i < named.NumMethods(); i++ { + if named.Method(i) == meth { + path = appendOpArg(path, opMethod, i) + return Path(path), true + } + } + + // Due to golang/go#59944, go/types fails to associate the receiver with + // certain methods on cgo types. + // + // TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go + // versions gopls supports. + return "", false + // panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named))) +} + +// find finds obj within type T, returning the path to it, or nil if not found. +// +// The seen map is used to short circuit cycles through type parameters. If +// nil, it will be allocated as necessary. +func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { + switch T := T.(type) { + case *types.Basic, *types.Named: + // Named types belonging to pkg were handled already, + // so T must belong to another package. No path. + return nil + case *types.Pointer: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Slice: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Array: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Chan: + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Map: + if r := find(obj, T.Key(), append(path, opKey), seen); r != nil { + return r + } + return find(obj, T.Elem(), append(path, opElem), seen) + case *types.Signature: + if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil { + return r + } + if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { + return r + } + return find(obj, T.Results(), append(path, opResults), seen) + case *types.Struct: + for i := 0; i < T.NumFields(); i++ { + fld := T.Field(i) + path2 := appendOpArg(path, opField, i) + if fld == obj { + return path2 // found field var + } + if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *types.Tuple: + for i := 0; i < T.Len(); i++ { + v := T.At(i) + path2 := appendOpArg(path, opAt, i) + if v == obj { + return path2 // found param/result var + } + if r := find(obj, v.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *types.Interface: + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return path2 // found interface method + } + if r := find(obj, m.Type(), append(path2, opType), seen); r != nil { + return r + } + } + return nil + case *typeparams.TypeParam: + name := T.Obj() + if name == obj { + return append(path, opObj) + } + if seen[name] { + return nil + } + if seen == nil { + seen = make(map[*types.TypeName]bool) + } + seen[name] = true + if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil { + return r + } + return nil + } + panic(T) +} + +func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { + for i := 0; i < list.Len(); i++ { + tparam := list.At(i) + path2 := appendOpArg(path, opTypeParam, i) + if r := find(obj, tparam, path2, seen); r != nil { + return r + } + } + return nil +} + +// Object returns the object denoted by path p within the package pkg. +func Object(pkg *types.Package, p Path) (types.Object, error) { + pathstr := string(p) + if pathstr == "" { + return nil, fmt.Errorf("empty path") + } + + var pkgobj, suffix string + if dot := strings.IndexByte(pathstr, opType); dot < 0 { + pkgobj = pathstr + } else { + pkgobj = pathstr[:dot] + suffix = pathstr[dot:] // suffix starts with "." + } + + obj := pkg.Scope().Lookup(pkgobj) + if obj == nil { + return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) + } + + // abstraction of *types.{Pointer,Slice,Array,Chan,Map} + type hasElem interface { + Elem() types.Type + } + // abstraction of *types.{Named,Signature} + type hasTypeParams interface { + TypeParams() *typeparams.TypeParamList + } + // abstraction of *types.{Named,TypeParam} + type hasObj interface { + Obj() *types.TypeName + } + + // The loop state is the pair (t, obj), + // exactly one of which is non-nil, initially obj. + // All suffixes start with '.' (the only object->type operation), + // followed by optional type->type operations, + // then a type->object operation. + // The cycle then repeats. + var t types.Type + for suffix != "" { + code := suffix[0] + suffix = suffix[1:] + + // Codes [AFM] have an integer operand. + var index int + switch code { + case opAt, opField, opMethod, opTypeParam: + rest := strings.TrimLeft(suffix, "0123456789") + numerals := suffix[:len(suffix)-len(rest)] + suffix = rest + i, err := strconv.Atoi(numerals) + if err != nil { + return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) + } + index = int(i) + case opObj: + // no operand + default: + // The suffix must end with a type->object operation. + if suffix == "" { + return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) + } + } + + if code == opType { + if t != nil { + return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) + } + t = obj.Type() + obj = nil + continue + } + + if t == nil { + return nil, fmt.Errorf("invalid path: code %q in object context", code) + } + + // Inv: t != nil, obj == nil + + switch code { + case opElem: + hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) + } + t = hasElem.Elem() + + case opKey: + mapType, ok := t.(*types.Map) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) + } + t = mapType.Key() + + case opParams: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Params() + + case opResults: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Results() + + case opUnderlying: + named, ok := t.(*types.Named) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t) + } + t = named.Underlying() + + case opTypeParam: + hasTypeParams, ok := t.(hasTypeParams) // Named, Signature + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t) + } + tparams := hasTypeParams.TypeParams() + if n := tparams.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + t = tparams.At(index) + + case opConstraint: + tparam, ok := t.(*typeparams.TypeParam) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) + } + t = tparam.Constraint() + + case opAt: + tuple, ok := t.(*types.Tuple) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t) + } + if n := tuple.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + obj = tuple.At(index) + t = nil + + case opField: + structType, ok := t.(*types.Struct) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) + } + if n := structType.NumFields(); index >= n { + return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) + } + obj = structType.Field(index) + t = nil + + case opMethod: + switch t := t.(type) { + case *types.Interface: + if index >= t.NumMethods() { + return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) + } + obj = t.Method(index) // Id-ordered + + case *types.Named: + if index >= t.NumMethods() { + return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) + } + obj = t.Method(index) + + default: + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) + } + t = nil + + case opObj: + hasObj, ok := t.(hasObj) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t) + } + obj = hasObj.Obj() + t = nil + + default: + return nil, fmt.Errorf("invalid path: unknown code %q", code) + } + } + + if obj.Pkg() != pkg { + return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) + } + + return obj, nil // success +} + +// scopeObjects is a memoization of scope objects. +// Callers must not modify the result. +func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object { + m := enc.scopeMemo + if m == nil { + m = make(map[*types.Scope][]types.Object) + enc.scopeMemo = m + } + objs, ok := m[scope] + if !ok { + names := scope.Names() // allocates and sorts + objs = make([]types.Object, len(names)) + for i, name := range names { + objs[i] = scope.Lookup(name) + } + m[scope] = objs + } + return objs +} diff --git a/vendor/golang.org/x/tools/imports/forward.go b/vendor/golang.org/x/tools/imports/forward.go index d2547c74..cb6db889 100644 --- a/vendor/golang.org/x/tools/imports/forward.go +++ b/vendor/golang.org/x/tools/imports/forward.go @@ -7,8 +7,8 @@ package imports // import "golang.org/x/tools/imports" import ( - "io/ioutil" "log" + "os" "golang.org/x/tools/internal/gocommand" intimp "golang.org/x/tools/internal/imports" @@ -44,7 +44,7 @@ var LocalPrefix string func Process(filename string, src []byte, opt *Options) ([]byte, error) { var err error if src == nil { - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/tools/internal/event/tag/tag.go b/vendor/golang.org/x/tools/internal/event/tag/tag.go index ff2f2ecd..581b26c2 100644 --- a/vendor/golang.org/x/tools/internal/event/tag/tag.go +++ b/vendor/golang.org/x/tools/internal/event/tag/tag.go @@ -19,7 +19,7 @@ var ( File = keys.NewString("file", "") Directory = keys.New("directory", "") URI = keys.New("URI", "") - Package = keys.NewString("package", "") // Package ID + Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs PackagePath = keys.NewString("package_path", "") Query = keys.New("query", "") Snapshot = keys.NewUInt64("snapshot", "") diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go deleted file mode 100644 index 798fe599..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package fastwalk provides a faster version of filepath.Walk for file system -// scanning tools. -package fastwalk - -import ( - "errors" - "os" - "path/filepath" - "runtime" - "sync" -) - -// ErrTraverseLink is used as a return value from WalkFuncs to indicate that the -// symlink named in the call may be traversed. -var ErrTraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory") - -// ErrSkipFiles is a used as a return value from WalkFuncs to indicate that the -// callback should not be called for any other files in the current directory. -// Child directories will still be traversed. -var ErrSkipFiles = errors.New("fastwalk: skip remaining files in directory") - -// Walk is a faster implementation of filepath.Walk. -// -// filepath.Walk's design necessarily calls os.Lstat on each file, -// even if the caller needs less info. -// Many tools need only the type of each file. -// On some platforms, this information is provided directly by the readdir -// system call, avoiding the need to stat each file individually. -// fastwalk_unix.go contains a fork of the syscall routines. -// -// See golang.org/issue/16399 -// -// Walk walks the file tree rooted at root, calling walkFn for -// each file or directory in the tree, including root. -// -// If fastWalk returns filepath.SkipDir, the directory is skipped. -// -// Unlike filepath.Walk: -// - file stat calls must be done by the user. -// The only provided metadata is the file type, which does not include -// any permission bits. -// - multiple goroutines stat the filesystem concurrently. The provided -// walkFn must be safe for concurrent use. -// - fastWalk can follow symlinks if walkFn returns the TraverseLink -// sentinel error. It is the walkFn's responsibility to prevent -// fastWalk from going into symlink cycles. -func Walk(root string, walkFn func(path string, typ os.FileMode) error) error { - // TODO(bradfitz): make numWorkers configurable? We used a - // minimum of 4 to give the kernel more info about multiple - // things we want, in hopes its I/O scheduling can take - // advantage of that. Hopefully most are in cache. Maybe 4 is - // even too low of a minimum. Profile more. - numWorkers := 4 - if n := runtime.NumCPU(); n > numWorkers { - numWorkers = n - } - - // Make sure to wait for all workers to finish, otherwise - // walkFn could still be called after returning. This Wait call - // runs after close(e.donec) below. - var wg sync.WaitGroup - defer wg.Wait() - - w := &walker{ - fn: walkFn, - enqueuec: make(chan walkItem, numWorkers), // buffered for performance - workc: make(chan walkItem, numWorkers), // buffered for performance - donec: make(chan struct{}), - - // buffered for correctness & not leaking goroutines: - resc: make(chan error, numWorkers), - } - defer close(w.donec) - - for i := 0; i < numWorkers; i++ { - wg.Add(1) - go w.doWork(&wg) - } - todo := []walkItem{{dir: root}} - out := 0 - for { - workc := w.workc - var workItem walkItem - if len(todo) == 0 { - workc = nil - } else { - workItem = todo[len(todo)-1] - } - select { - case workc <- workItem: - todo = todo[:len(todo)-1] - out++ - case it := <-w.enqueuec: - todo = append(todo, it) - case err := <-w.resc: - out-- - if err != nil { - return err - } - if out == 0 && len(todo) == 0 { - // It's safe to quit here, as long as the buffered - // enqueue channel isn't also readable, which might - // happen if the worker sends both another unit of - // work and its result before the other select was - // scheduled and both w.resc and w.enqueuec were - // readable. - select { - case it := <-w.enqueuec: - todo = append(todo, it) - default: - return nil - } - } - } - } -} - -// doWork reads directories as instructed (via workc) and runs the -// user's callback function. -func (w *walker) doWork(wg *sync.WaitGroup) { - defer wg.Done() - for { - select { - case <-w.donec: - return - case it := <-w.workc: - select { - case <-w.donec: - return - case w.resc <- w.walk(it.dir, !it.callbackDone): - } - } - } -} - -type walker struct { - fn func(path string, typ os.FileMode) error - - donec chan struct{} // closed on fastWalk's return - workc chan walkItem // to workers - enqueuec chan walkItem // from workers - resc chan error // from workers -} - -type walkItem struct { - dir string - callbackDone bool // callback already called; don't do it again -} - -func (w *walker) enqueue(it walkItem) { - select { - case w.enqueuec <- it: - case <-w.donec: - } -} - -func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { - joined := dirName + string(os.PathSeparator) + baseName - if typ == os.ModeDir { - w.enqueue(walkItem{dir: joined}) - return nil - } - - err := w.fn(joined, typ) - if typ == os.ModeSymlink { - if err == ErrTraverseLink { - // Set callbackDone so we don't call it twice for both the - // symlink-as-symlink and the symlink-as-directory later: - w.enqueue(walkItem{dir: joined, callbackDone: true}) - return nil - } - if err == filepath.SkipDir { - // Permit SkipDir on symlinks too. - return nil - } - } - return err -} - -func (w *walker) walk(root string, runUserCallback bool) error { - if runUserCallback { - err := w.fn(root, os.ModeDir) - if err == filepath.SkipDir { - return nil - } - if err != nil { - return err - } - } - - return readDir(root, w.onDirEnt) -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go deleted file mode 100644 index 0ca55e0d..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build darwin && cgo -// +build darwin,cgo - -package fastwalk - -/* -#include - -// fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent** -// result pointer which triggers CGO's "Go pointer to Go pointer" check unless -// we allocat the result dirent* with malloc. -// -// fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the -// directory, or a positive error number to indicate failure. -static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) { - struct dirent *result; - int ret = readdir_r(fd, entry, &result); - if (ret == 0 && result == NULL) { - ret = -1; // EOF - } - return ret; -} -*/ -import "C" - -import ( - "os" - "syscall" - "unsafe" -) - -func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { - fd, err := openDir(dirName) - if err != nil { - return &os.PathError{Op: "opendir", Path: dirName, Err: err} - } - defer C.closedir(fd) - - skipFiles := false - var dirent syscall.Dirent - for { - ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent)))) - if ret != 0 { - if ret == -1 { - break // EOF - } - if ret == int(syscall.EINTR) { - continue - } - return &os.PathError{Op: "readdir", Path: dirName, Err: syscall.Errno(ret)} - } - if dirent.Ino == 0 { - continue - } - typ := dtToType(dirent.Type) - if skipFiles && typ.IsRegular() { - continue - } - name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] - name = name[:dirent.Namlen] - for i, c := range name { - if c == 0 { - name = name[:i] - break - } - } - // Check for useless names before allocating a string. - if string(name) == "." || string(name) == ".." { - continue - } - if err := fn(dirName, string(name), typ); err != nil { - if err != ErrSkipFiles { - return err - } - skipFiles = true - } - } - - return nil -} - -func dtToType(typ uint8) os.FileMode { - switch typ { - case syscall.DT_BLK: - return os.ModeDevice - case syscall.DT_CHR: - return os.ModeDevice | os.ModeCharDevice - case syscall.DT_DIR: - return os.ModeDir - case syscall.DT_FIFO: - return os.ModeNamedPipe - case syscall.DT_LNK: - return os.ModeSymlink - case syscall.DT_REG: - return 0 - case syscall.DT_SOCK: - return os.ModeSocket - } - return ^os.FileMode(0) -} - -// openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR -// needs to be closed with closedir(3). -func openDir(path string) (*C.DIR, error) { - name, err := syscall.BytePtrFromString(path) - if err != nil { - return nil, err - } - for { - fd, err := C.opendir((*C.char)(unsafe.Pointer(name))) - if err != syscall.EINTR { - return fd, err - } - } -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go deleted file mode 100644 index d58595db..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build freebsd || openbsd || netbsd -// +build freebsd openbsd netbsd - -package fastwalk - -import "syscall" - -func direntInode(dirent *syscall.Dirent) uint64 { - return uint64(dirent.Fileno) -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go deleted file mode 100644 index d3922890..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build (linux || (darwin && !cgo)) && !appengine -// +build linux darwin,!cgo -// +build !appengine - -package fastwalk - -import "syscall" - -func direntInode(dirent *syscall.Dirent) uint64 { - return dirent.Ino -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go deleted file mode 100644 index 38a4db6a..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build (darwin && !cgo) || freebsd || openbsd || netbsd -// +build darwin,!cgo freebsd openbsd netbsd - -package fastwalk - -import "syscall" - -func direntNamlen(dirent *syscall.Dirent) uint64 { - return uint64(dirent.Namlen) -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go deleted file mode 100644 index c82e57df..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_linux.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build linux && !appengine -// +build linux,!appengine - -package fastwalk - -import ( - "bytes" - "syscall" - "unsafe" -) - -func direntNamlen(dirent *syscall.Dirent) uint64 { - const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name)) - nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) - const nameBufLen = uint16(len(nameBuf)) - limit := dirent.Reclen - fixedHdr - if limit > nameBufLen { - limit = nameBufLen - } - nameLen := bytes.IndexByte(nameBuf[:limit], 0) - if nameLen < 0 { - panic("failed to find terminating 0 byte in dirent") - } - return uint64(nameLen) -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go deleted file mode 100644 index 085d3116..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build appengine || (!linux && !darwin && !freebsd && !openbsd && !netbsd) -// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd - -package fastwalk - -import ( - "io/ioutil" - "os" -) - -// readDir calls fn for each directory entry in dirName. -// It does not descend into directories or follow symlinks. -// If fn returns a non-nil error, readDir returns with that error -// immediately. -func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { - fis, err := ioutil.ReadDir(dirName) - if err != nil { - return err - } - skipFiles := false - for _, fi := range fis { - if fi.Mode().IsRegular() && skipFiles { - continue - } - if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { - if err == ErrSkipFiles { - skipFiles = true - continue - } - return err - } - } - return nil -} diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go deleted file mode 100644 index f12f1a73..00000000 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build (linux || freebsd || openbsd || netbsd || (darwin && !cgo)) && !appengine -// +build linux freebsd openbsd netbsd darwin,!cgo -// +build !appengine - -package fastwalk - -import ( - "fmt" - "os" - "syscall" - "unsafe" -) - -const blockSize = 8 << 10 - -// unknownFileMode is a sentinel (and bogus) os.FileMode -// value used to represent a syscall.DT_UNKNOWN Dirent.Type. -const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice - -func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { - fd, err := open(dirName, 0, 0) - if err != nil { - return &os.PathError{Op: "open", Path: dirName, Err: err} - } - defer syscall.Close(fd) - - // The buffer must be at least a block long. - buf := make([]byte, blockSize) // stack-allocated; doesn't escape - bufp := 0 // starting read position in buf - nbuf := 0 // end valid data in buf - skipFiles := false - for { - if bufp >= nbuf { - bufp = 0 - nbuf, err = readDirent(fd, buf) - if err != nil { - return os.NewSyscallError("readdirent", err) - } - if nbuf <= 0 { - return nil - } - } - consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) - bufp += consumed - if name == "" || name == "." || name == ".." { - continue - } - // Fallback for filesystems (like old XFS) that don't - // support Dirent.Type and have DT_UNKNOWN (0) there - // instead. - if typ == unknownFileMode { - fi, err := os.Lstat(dirName + "/" + name) - if err != nil { - // It got deleted in the meantime. - if os.IsNotExist(err) { - continue - } - return err - } - typ = fi.Mode() & os.ModeType - } - if skipFiles && typ.IsRegular() { - continue - } - if err := fn(dirName, name, typ); err != nil { - if err == ErrSkipFiles { - skipFiles = true - continue - } - return err - } - } -} - -func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { - // golang.org/issue/37269 - dirent := &syscall.Dirent{} - copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf) - if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { - panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) - } - if len(buf) < int(dirent.Reclen) { - panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) - } - consumed = int(dirent.Reclen) - if direntInode(dirent) == 0 { // File absent in directory. - return - } - switch dirent.Type { - case syscall.DT_REG: - typ = 0 - case syscall.DT_DIR: - typ = os.ModeDir - case syscall.DT_LNK: - typ = os.ModeSymlink - case syscall.DT_BLK: - typ = os.ModeDevice - case syscall.DT_FIFO: - typ = os.ModeNamedPipe - case syscall.DT_SOCK: - typ = os.ModeSocket - case syscall.DT_UNKNOWN: - typ = unknownFileMode - default: - // Skip weird things. - // It's probably a DT_WHT (http://lwn.net/Articles/325369/) - // or something. Revisit if/when this package is moved outside - // of goimports. goimports only cares about regular files, - // symlinks, and directories. - return - } - - nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) - nameLen := direntNamlen(dirent) - - // Special cases for common things: - if nameLen == 1 && nameBuf[0] == '.' { - name = "." - } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { - name = ".." - } else { - name = string(nameBuf[:nameLen]) - } - return -} - -// According to https://golang.org/doc/go1.14#runtime -// A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS -// systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases. -// -// This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors. -// We need to retry in this case. -func open(path string, mode int, perm uint32) (fd int, err error) { - for { - fd, err := syscall.Open(path, mode, perm) - if err != syscall.EINTR { - return fd, err - } - } -} - -func readDirent(fd int, buf []byte) (n int, err error) { - for { - nbuf, err := syscall.ReadDirent(fd, buf) - if err != syscall.EINTR { - return nbuf, err - } - } -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index b1223713..2d078ccb 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -29,7 +29,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func switch hdr { case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + data, err = io.ReadAll(buf) if err != nil { break } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index 9930d8c3..6103dd71 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -22,17 +22,23 @@ import ( "strconv" "strings" + "golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/internal/tokeninternal" "golang.org/x/tools/internal/typeparams" ) // IExportShallow encodes "shallow" export data for the specified package. // -// No promises are made about the encoding other than that it can be -// decoded by the same version of IIExportShallow. If you plan to save -// export data in the file system, be sure to include a cryptographic -// digest of the executable in the key to avoid version skew. -func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { +// No promises are made about the encoding other than that it can be decoded by +// the same version of IIExportShallow. If you plan to save export data in the +// file system, be sure to include a cryptographic digest of the executable in +// the key to avoid version skew. +// +// If the provided reportf func is non-nil, it will be used for reporting bugs +// encountered during export. +// TODO(rfindley): remove reportf when we are confident enough in the new +// objectpath encoding. +func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) { // In principle this operation can only fail if out.Write fails, // but that's impossible for bytes.Buffer---and as a matter of // fact iexportCommon doesn't even check for I/O errors. @@ -47,19 +53,27 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { // IImportShallow decodes "shallow" types.Package data encoded by // IExportShallow in the same executable. This function cannot import data from // cmd/compile or gcexportdata.Write. -func IImportShallow(fset *token.FileSet, getPackage GetPackageFunc, data []byte, path string, insert InsertType) (*types.Package, error) { +// +// The importer calls getPackages to obtain package symbols for all +// packages mentioned in the export data, including the one being +// decoded. +// +// If the provided reportf func is non-nil, it will be used for reporting bugs +// encountered during import. +// TODO(rfindley): remove reportf when we are confident enough in the new +// objectpath encoding. +func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) { const bundle = false - pkgs, err := iimportCommon(fset, getPackage, data, bundle, path, insert) + const shallow = true + pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf) if err != nil { return nil, err } return pkgs[0], nil } -// InsertType is the type of a function that creates a types.TypeName -// object for a named type and inserts it into the scope of the -// specified Package. -type InsertType = func(pkg *types.Package, name string) +// ReportFunc is the type of a function used to report formatted bugs. +type ReportFunc = func(string, ...interface{}) // Current bundled export format version. Increase with each format change. // 0: initial implementation @@ -313,8 +327,9 @@ type iexporter struct { out *bytes.Buffer version int - shallow bool // don't put types from other packages in the index - localpkg *types.Package // (nil in bundle mode) + shallow bool // don't put types from other packages in the index + objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated + localpkg *types.Package // (nil in bundle mode) // allPkgs tracks all packages that have been referenced by // the export data, so we can ensure to include them in the @@ -354,6 +369,17 @@ func (p *iexporter) trace(format string, args ...interface{}) { fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) } +// objectpathEncoder returns the lazily allocated objectpath.Encoder to use +// when encoding objects in other packages during shallow export. +// +// Using a shared Encoder amortizes some of cost of objectpath search. +func (p *iexporter) objectpathEncoder() *objectpath.Encoder { + if p.objEncoder == nil { + p.objEncoder = new(objectpath.Encoder) + } + return p.objEncoder +} + // stringOff returns the offset of s within the string section. // If not already present, it's added to the end. func (p *iexporter) stringOff(s string) uint64 { @@ -413,7 +439,6 @@ type exportWriter struct { p *iexporter data intWriter - currPkg *types.Package prevFile string prevLine int64 prevColumn int64 @@ -436,7 +461,6 @@ func (p *iexporter) doDecl(obj types.Object) { }() } w := p.newWriter() - w.setPkg(obj.Pkg(), false) switch obj := obj.(type) { case *types.Var: @@ -673,6 +697,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) { w.pkg(obj.Pkg()) } +// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass +// it in explicitly into signatures and structs that may use it for +// constructing fields. func (w *exportWriter) typ(t types.Type, pkg *types.Package) { w.data.uint64(w.p.typOff(t, pkg)) } @@ -764,30 +791,53 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { case *types.Signature: w.startType(signatureType) - w.setPkg(pkg, true) + w.pkg(pkg) w.signature(t) case *types.Struct: w.startType(structType) n := t.NumFields() + // Even for struct{} we must emit some qualifying package, because that's + // what the compiler does, and thus that's what the importer expects. + fieldPkg := pkg if n > 0 { - w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects - } else { - w.setPkg(pkg, true) + fieldPkg = t.Field(0).Pkg() } + if fieldPkg == nil { + // TODO(rfindley): improve this very hacky logic. + // + // The importer expects a package to be set for all struct types, even + // those with no fields. A better encoding might be to set NumFields + // before pkg. setPkg panics with a nil package, which may be possible + // to reach with invalid packages (and perhaps valid packages, too?), so + // (arbitrarily) set the localpkg if available. + // + // Alternatively, we may be able to simply guarantee that pkg != nil, by + // reconsidering the encoding of constant values. + if w.p.shallow { + fieldPkg = w.p.localpkg + } else { + panic(internalErrorf("no package to set for empty struct")) + } + } + w.pkg(fieldPkg) w.uint64(uint64(n)) + for i := 0; i < n; i++ { f := t.Field(i) + if w.p.shallow { + w.objectPath(f) + } w.pos(f.Pos()) w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg - w.typ(f.Type(), pkg) + w.typ(f.Type(), fieldPkg) w.bool(f.Anonymous()) w.string(t.Tag(i)) // note (or tag) } case *types.Interface: w.startType(interfaceType) - w.setPkg(pkg, true) + w.pkg(pkg) n := t.NumEmbeddeds() w.uint64(uint64(n)) @@ -802,10 +852,16 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { w.typ(ft, tPkg) } + // See comment for struct fields. In shallow mode we change the encoding + // for interface methods that are promoted from other packages. + n = t.NumExplicitMethods() w.uint64(uint64(n)) for i := 0; i < n; i++ { m := t.ExplicitMethod(i) + if w.p.shallow { + w.objectPath(m) + } w.pos(m.Pos()) w.string(m.Name()) sig, _ := m.Type().(*types.Signature) @@ -827,12 +883,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { } } -func (w *exportWriter) setPkg(pkg *types.Package, write bool) { - if write { - w.pkg(pkg) +// objectPath writes the package and objectPath to use to look up obj in a +// different package, when encoding in "shallow" mode. +// +// When doing a shallow import, the importer creates only the local package, +// and requests package symbols for dependencies from the client. +// However, certain types defined in the local package may hold objects defined +// (perhaps deeply) within another package. +// +// For example, consider the following: +// +// package a +// func F() chan * map[string] struct { X int } +// +// package b +// import "a" +// var B = a.F() +// +// In this example, the type of b.B holds fields defined in package a. +// In order to have the correct canonical objects for the field defined in the +// type of B, they are encoded as objectPaths and later looked up in the +// importer. The same problem applies to interface methods. +func (w *exportWriter) objectPath(obj types.Object) { + if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg { + // obj.Pkg() may be nil for the builtin error.Error. + // In this case, or if obj is declared in the local package, no need to + // encode. + w.string("") + return } - - w.currPkg = pkg + objectPath, err := w.p.objectpathEncoder().For(obj) + if err != nil { + // Fall back to the empty string, which will cause the importer to create a + // new object, which matches earlier behavior. Creating a new object is + // sufficient for many purposes (such as type checking), but causes certain + // references algorithms to fail (golang/go#60819). However, we didn't + // notice this problem during months of gopls@v0.12.0 testing. + // + // TODO(golang/go#61674): this workaround is insufficient, as in the case + // where the field forwarded from an instantiated type that may not appear + // in the export data of the original package: + // + // // package a + // type A[P any] struct{ F P } + // + // // package b + // type B a.A[int] + // + // We need to update references algorithms not to depend on this + // de-duplication, at which point we may want to simply remove the + // workaround here. + w.string("") + return + } + w.string(string(objectPath)) + w.pkg(obj.Pkg()) } func (w *exportWriter) signature(sig *types.Signature) { @@ -913,6 +1018,17 @@ func (w *exportWriter) value(typ types.Type, v constant.Value) { w.int64(int64(v.Kind())) } + if v.Kind() == constant.Unknown { + // golang/go#60605: treat unknown constant values as if they have invalid type + // + // This loses some fidelity over the package type-checked from source, but that + // is acceptable. + // + // TODO(rfindley): we should switch on the recorded constant kind rather + // than the constant type + return + } + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { case types.IsBoolean: w.bool(constant.BoolVal(v)) @@ -1194,6 +1310,13 @@ type internalError string func (e internalError) Error() string { return "gcimporter: " + string(e) } +// TODO(adonovan): make this call panic, so that it's symmetric with errorf. +// Otherwise it's easy to forget to do anything with the error. +// +// TODO(adonovan): also, consider switching the names "errorf" and +// "internalErrorf" as the former is used for bugs, whose cause is +// internal inconsistency, whereas the latter is used for ordinary +// situations like bad input, whose cause is external. func internalErrorf(format string, args ...interface{}) error { return internalError(fmt.Sprintf(format, args...)) } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index 94a5eba3..8e64cf64 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -21,6 +21,7 @@ import ( "sort" "strings" + "golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/internal/typeparams" ) @@ -85,7 +86,7 @@ const ( // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { - pkgs, err := iimportCommon(fset, GetPackageFromMap(imports), data, false, path, nil) + pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil) if err != nil { return 0, nil, err } @@ -94,33 +95,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // IImportBundle imports a set of packages from the serialized package bundle. func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { - return iimportCommon(fset, GetPackageFromMap(imports), data, true, "", nil) + return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil) } -// A GetPackageFunc is a function that gets the package with the given path -// from the importer state, creating it (with the specified name) if necessary. -// It is an abstraction of the map historically used to memoize package creation. +// A GetPackagesFunc function obtains the non-nil symbols for a set of +// packages, creating and recursively importing them as needed. An +// implementation should store each package symbol is in the Pkg +// field of the items array. // -// Two calls with the same path must return the same package. -// -// If the given getPackage func returns nil, the import will fail. -type GetPackageFunc = func(path, name string) *types.Package +// Any error causes importing to fail. This can be used to quickly read +// the import manifest of an export data file without fully decoding it. +type GetPackagesFunc = func(items []GetPackagesItem) error + +// A GetPackagesItem is a request from the importer for the package +// symbol of the specified name and path. +type GetPackagesItem struct { + Name, Path string + Pkg *types.Package // to be filled in by GetPackagesFunc call + + // private importer state + pathOffset uint64 + nameIndex map[string]uint64 +} -// GetPackageFromMap returns a GetPackageFunc that retrieves packages from the -// given map of package path -> package. +// GetPackagesFromMap returns a GetPackagesFunc that retrieves +// packages from the given map of package path to package. // -// The resulting func may mutate m: if a requested package is not found, a new -// package will be inserted into m. -func GetPackageFromMap(m map[string]*types.Package) GetPackageFunc { - return func(path, name string) *types.Package { - if _, ok := m[path]; !ok { - m[path] = types.NewPackage(path, name) +// The returned function may mutate m: each requested package that is not +// found is created with types.NewPackage and inserted into m. +func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc { + return func(items []GetPackagesItem) error { + for i, item := range items { + pkg, ok := m[item.Path] + if !ok { + pkg = types.NewPackage(item.Path, item.Name) + m[item.Path] = pkg + } + items[i].Pkg = pkg } - return m[path] + return nil } } -func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) { +func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) { const currentVersion = iexportVersionCurrent version := int64(-1) if !debug { @@ -159,7 +176,7 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, sLen := int64(r.uint64()) var fLen int64 var fileOffset []uint64 - if insert != nil { + if shallow { // Shallow mode uses a different position encoding. fLen = int64(r.uint64()) fileOffset = make([]uint64, r.uint64()) @@ -178,7 +195,8 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, p := iimporter{ version: int(version), ipath: path, - insert: insert, + shallow: shallow, + reportf: reportf, stringData: stringData, stringCache: make(map[uint64]string), @@ -205,8 +223,9 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, p.typCache[uint64(i)] = pt } - pkgList := make([]*types.Package, r.uint64()) - for i := range pkgList { + // Gather the relevant packages from the manifest. + items := make([]GetPackagesItem, r.uint64()) + for i := range items { pkgPathOff := r.uint64() pkgPath := p.stringAt(pkgPathOff) pkgName := p.stringAt(r.uint64()) @@ -215,29 +234,42 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, if pkgPath == "" { pkgPath = path } - pkg := getPackage(pkgPath, pkgName) - if pkg == nil { - errorf("internal error: getPackage returned nil package for %s", pkgPath) - } else if pkg.Name() != pkgName { - errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) - } - if i == 0 && !bundle { - p.localpkg = pkg - } - - p.pkgCache[pkgPathOff] = pkg + items[i].Name = pkgName + items[i].Path = pkgPath + items[i].pathOffset = pkgPathOff // Read index for package. nameIndex := make(map[string]uint64) nSyms := r.uint64() - // In shallow mode we don't expect an index for other packages. - assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil) + // In shallow mode, only the current package (i=0) has an index. + assert(!(shallow && i > 0 && nSyms != 0)) for ; nSyms > 0; nSyms-- { name := p.stringAt(r.uint64()) nameIndex[name] = r.uint64() } - p.pkgIndex[pkg] = nameIndex + items[i].nameIndex = nameIndex + } + + // Request packages all at once from the client, + // enabling a parallel implementation. + if err := getPackages(items); err != nil { + return nil, err // don't wrap this error + } + + // Check the results and complete the index. + pkgList := make([]*types.Package, len(items)) + for i, item := range items { + pkg := item.Pkg + if pkg == nil { + errorf("internal error: getPackages returned nil package for %q", item.Path) + } else if pkg.Path() != item.Path { + errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path) + } else if pkg.Name() != item.Name { + errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name) + } + p.pkgCache[item.pathOffset] = pkg + p.pkgIndex[pkg] = item.nameIndex pkgList[i] = pkg } @@ -296,6 +328,13 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, typ.Complete() } + // Workaround for golang/go#61561. See the doc for instanceList for details. + for _, typ := range p.instanceList { + if iface, _ := typ.Underlying().(*types.Interface); iface != nil { + iface.Complete() + } + } + return pkgs, nil } @@ -308,8 +347,8 @@ type iimporter struct { version int ipath string - localpkg *types.Package - insert func(pkg *types.Package, name string) // "shallow" mode only + shallow bool + reportf ReportFunc // if non-nil, used to report bugs stringData []byte stringCache map[uint64]string @@ -326,6 +365,12 @@ type iimporter struct { fake fakeFileSet interfaceList []*types.Interface + // Workaround for the go/types bug golang/go#61561: instances produced during + // instantiation may contain incomplete interfaces. Here we only complete the + // underlying type of the instance, which is the most common case but doesn't + // handle parameterized interface literals defined deeper in the type. + instanceList []types.Type // instances for later completion (see golang/go#61561) + // Arguments for calls to SetConstraint that are deferred due to recursive types later []setConstraintArgs @@ -357,13 +402,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) { off, ok := p.pkgIndex[pkg][name] if !ok { - // In "shallow" mode, call back to the application to - // find the object and insert it into the package scope. - if p.insert != nil { - assert(pkg != p.localpkg) - p.insert(pkg, name) // "can't fail" - return - } + // In deep mode, the index should be complete. In shallow + // mode, we should have already recursively loaded necessary + // dependencies so the above Lookup succeeds. errorf("%v.%v not in index", pkg, name) } @@ -730,7 +771,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) { } func (r *importReader) pos() token.Pos { - if r.p.insert != nil { // shallow mode + if r.p.shallow { + // precise offsets are encoded only in shallow mode return r.posv2() } if r.p.version >= iexportVersionPosCol { @@ -831,13 +873,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { fields := make([]*types.Var, r.uint64()) tags := make([]string, len(fields)) for i := range fields { + var field *types.Var + if r.p.shallow { + field, _ = r.objectPathObject().(*types.Var) + } + fpos := r.pos() fname := r.ident() ftyp := r.typ() emb := r.bool() tag := r.string() - fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + // Either this is not a shallow import, the field is local, or the + // encoded objectPath failed to produce an object (a bug). + // + // Even in this last, buggy case, fall back on creating a new field. As + // discussed in iexport.go, this is not correct, but mostly works and is + // preferable to failing (for now at least). + if field == nil { + field = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + } + + fields[i] = field tags[i] = tag } return types.NewStruct(fields, tags) @@ -853,6 +910,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { methods := make([]*types.Func, r.uint64()) for i := range methods { + var method *types.Func + if r.p.shallow { + method, _ = r.objectPathObject().(*types.Func) + } + mpos := r.pos() mname := r.ident() @@ -862,9 +924,12 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { if base != nil { recv = types.NewVar(token.NoPos, r.currPkg, "", base) } - msig := r.signature(recv, nil, nil) - methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) + + if method == nil { + method = types.NewFunc(mpos, r.currPkg, mname, msig) + } + methods[i] = method } typ := newInterface(methods, embeddeds) @@ -902,6 +967,9 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { // we must always use the methods of the base (orig) type. // TODO provide a non-nil *Environment t, _ := typeparams.Instantiate(nil, baseType, targs, false) + + // Workaround for golang/go#61561. See the doc for instanceList for details. + r.p.instanceList = append(r.p.instanceList, t) return t case unionType: @@ -920,6 +988,26 @@ func (r *importReader) kind() itag { return itag(r.uint64()) } +// objectPathObject is the inverse of exportWriter.objectPath. +// +// In shallow mode, certain fields and methods may need to be looked up in an +// imported package. See the doc for exportWriter.objectPath for a full +// explanation. +func (r *importReader) objectPathObject() types.Object { + objPath := objectpath.Path(r.string()) + if objPath == "" { + return nil + } + pkg := r.pkg() + obj, err := objectpath.Object(pkg, objPath) + if err != nil { + if r.p.reportf != nil { + r.p.reportf("failed to find object for objectPath %q: %v", objPath, err) + } + } + return obj +} + func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { params := r.paramList() results := r.paramList() diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index 8d9fc98d..c27b91f8 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -85,6 +85,7 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde // RunRaw runs the invocation, serializing requests only if they fight over // go.mod changes. +// Postcondition: both error results have same nilness. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) defer done() @@ -95,23 +96,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) // If we encounter a load concurrency error, we need to retry serially. - if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { - return stdout, stderr, friendlyErr, err + if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) { + event.Error(ctx, "Load concurrency error, will retry serially", err) + + // Run serially by calling runPiped. + stdout.Reset() + stderr.Reset() + friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) } - event.Error(ctx, "Load concurrency error, will retry serially", err) - // Run serially by calling runPiped. - stdout.Reset() - stderr.Reset() - friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { // Wait for 1 worker to become available. select { case <-ctx.Done(): - return nil, nil, nil, ctx.Err() + return nil, nil, ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: defer func() { <-runner.inFlight }() } @@ -121,6 +123,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { // Make sure the runner is always initialized. runner.initialize() @@ -129,7 +132,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde // runPiped commands. select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.serialized <- struct{}{}: defer func() { <-runner.serialized }() } @@ -139,7 +142,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde for i := 0; i < maxInFlight; i++ { select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: // Make sure we always "return" any workers we took. defer func() { <-runner.inFlight }() @@ -172,6 +175,7 @@ type Invocation struct { Logf func(format string, args ...interface{}) } +// Postcondition: both error results have same nilness. func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { rawError = i.run(ctx, stdout, stderr) if rawError != nil { @@ -319,7 +323,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { // Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close // should cause the Read call in io.Copy to unblock and return // immediately, but we still need to receive from stdoutErr to confirm - // that that has happened. + // that it has happened. <-stdoutErr err2 = ctx.Err() } @@ -333,7 +337,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { // one goroutine at a time will call Write.” // // Since we're starting a goroutine that writes to cmd.Stdout, we must - // also update cmd.Stderr so that that still holds. + // also update cmd.Stderr so that it still holds. func() { defer func() { recover() }() if cmd.Stderr == prevStdout { diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go index 16840532..f79dd8cc 100644 --- a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -9,15 +9,12 @@ package gopathwalk import ( "bufio" "bytes" - "fmt" - "io/ioutil" + "io/fs" "log" "os" "path/filepath" "strings" "time" - - "golang.org/x/tools/internal/fastwalk" ) // Options controls the behavior of a Walk call. @@ -78,21 +75,36 @@ func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) } start := time.Now() if opts.Logf != nil { - opts.Logf("gopathwalk: scanning %s", root.Path) + opts.Logf("scanning %s", root.Path) } + w := &walker{ - root: root, - add: add, - skip: skip, - opts: opts, + root: root, + add: add, + skip: skip, + opts: opts, + added: make(map[string]bool), } w.init() - if err := fastwalk.Walk(root.Path, w.walk); err != nil { - log.Printf("gopathwalk: scanning directory %v: %v", root.Path, err) + + // Add a trailing path separator to cause filepath.WalkDir to traverse symlinks. + path := root.Path + if len(path) == 0 { + path = "." + string(filepath.Separator) + } else if !os.IsPathSeparator(path[len(path)-1]) { + path = path + string(filepath.Separator) + } + + if err := filepath.WalkDir(path, w.walk); err != nil { + logf := opts.Logf + if logf == nil { + logf = log.Printf + } + logf("scanning directory %v: %v", root.Path, err) } if opts.Logf != nil { - opts.Logf("gopathwalk: scanned %s in %v", root.Path, time.Since(start)) + opts.Logf("scanned %s in %v", root.Path, time.Since(start)) } } @@ -103,7 +115,9 @@ type walker struct { skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true. opts Options // Options passed to Walk by the user. - ignoredDirs []os.FileInfo // The ignored directories, loaded from .goimportsignore files. + ignoredDirs []string + + added map[string]bool } // init initializes the walker based on its Options @@ -119,13 +133,9 @@ func (w *walker) init() { for _, p := range ignoredPaths { full := filepath.Join(w.root.Path, p) - if fi, err := os.Stat(full); err == nil { - w.ignoredDirs = append(w.ignoredDirs, fi) - if w.opts.Logf != nil { - w.opts.Logf("Directory added to ignore list: %s", full) - } - } else if w.opts.Logf != nil { - w.opts.Logf("Error statting ignored directory: %v", err) + w.ignoredDirs = append(w.ignoredDirs, full) + if w.opts.Logf != nil { + w.opts.Logf("Directory added to ignore list: %s", full) } } } @@ -135,7 +145,7 @@ func (w *walker) init() { // The provided path is one of the $GOPATH entries with "src" appended. func (w *walker) getIgnoredDirs(path string) []string { file := filepath.Join(path, ".goimportsignore") - slurp, err := ioutil.ReadFile(file) + slurp, err := os.ReadFile(file) if w.opts.Logf != nil { if err != nil { w.opts.Logf("%v", err) @@ -160,9 +170,9 @@ func (w *walker) getIgnoredDirs(path string) []string { } // shouldSkipDir reports whether the file should be skipped or not. -func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool { +func (w *walker) shouldSkipDir(dir string) bool { for _, ignoredDir := range w.ignoredDirs { - if os.SameFile(fi, ignoredDir) { + if dir == ignoredDir { return true } } @@ -174,20 +184,25 @@ func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool { } // walk walks through the given path. -func (w *walker) walk(path string, typ os.FileMode) error { +func (w *walker) walk(path string, d fs.DirEntry, err error) error { + typ := d.Type() if typ.IsRegular() { + if !strings.HasSuffix(path, ".go") { + return nil + } + dir := filepath.Dir(path) if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) { // Doesn't make sense to have regular files // directly in your $GOPATH/src or $GOROOT/src. - return fastwalk.ErrSkipFiles - } - if !strings.HasSuffix(path, ".go") { return nil } - w.add(w.root, dir) - return fastwalk.ErrSkipFiles + if !w.added[dir] { + w.add(w.root, dir) + w.added[dir] = true + } + return nil } if typ == os.ModeDir { base := filepath.Base(path) @@ -197,20 +212,66 @@ func (w *walker) walk(path string, typ os.FileMode) error { (!w.opts.ModulesEnabled && base == "node_modules") { return filepath.SkipDir } - fi, err := os.Lstat(path) - if err == nil && w.shouldSkipDir(fi, path) { + if w.shouldSkipDir(path) { return filepath.SkipDir } return nil } - if typ == os.ModeSymlink { + if typ == os.ModeSymlink && err == nil { + // TODO(bcmills): 'go list all' itself ignores symlinks within GOROOT/src + // and GOPATH/src. Do we really need to traverse them here? If so, why? + + if os.IsPathSeparator(path[len(path)-1]) { + // The OS was supposed to resolve a directory symlink but didn't. + // + // On macOS this may be caused by a known libc/kernel bug; + // see https://go.dev/issue/59586. + // + // On Windows before Go 1.21, this may be caused by a bug in + // os.Lstat (fixed in https://go.dev/cl/463177). + // + // In either case, we can work around the bug by walking this level + // explicitly: first the symlink target itself, then its contents. + + fi, err := os.Stat(path) + if err != nil || !fi.IsDir() { + return nil + } + err = w.walk(path, fs.FileInfoToDirEntry(fi), nil) + if err == filepath.SkipDir { + return nil + } else if err != nil { + return err + } + + ents, _ := os.ReadDir(path) // ignore error if unreadable + for _, d := range ents { + nextPath := filepath.Join(path, d.Name()) + var err error + if d.IsDir() { + err = filepath.WalkDir(nextPath, w.walk) + } else { + err = w.walk(nextPath, d, nil) + if err == filepath.SkipDir { + break + } + } + if err != nil { + return err + } + } + return nil + } + base := filepath.Base(path) if strings.HasPrefix(base, ".#") { // Emacs noise. return nil } if w.shouldTraverse(path) { - return fastwalk.ErrTraverseLink + // Add a trailing separator to traverse the symlink. + nextPath := path + string(filepath.Separator) + return filepath.WalkDir(nextPath, w.walk) } } return nil @@ -220,17 +281,23 @@ func (w *walker) walk(path string, typ os.FileMode) error { // should be followed. It makes sure symlinks were never visited // before to avoid symlink loops. func (w *walker) shouldTraverse(path string) bool { + if w.shouldSkipDir(path) { + return false + } + ts, err := os.Stat(path) if err != nil { - fmt.Fprintln(os.Stderr, err) + logf := w.opts.Logf + if logf == nil { + logf = log.Printf + } + logf("%v", err) return false } if !ts.IsDir() { return false } - if w.shouldSkipDir(ts, filepath.Dir(path)) { - return false - } + // Check for symlink loops by statting each directory component // and seeing if any are the same file as ts. for { diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index d4f1b4e8..01e8ba5f 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -13,6 +13,7 @@ import ( "go/build" "go/parser" "go/token" + "io/fs" "io/ioutil" "os" "path" @@ -107,7 +108,7 @@ func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File { considerTests := strings.HasSuffix(filename, "_test.go") fileBase := filepath.Base(filename) - packageFileInfos, err := ioutil.ReadDir(srcDir) + packageFileInfos, err := os.ReadDir(srcDir) if err != nil { return nil } @@ -1469,11 +1470,11 @@ func VendorlessPath(ipath string) string { func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) { // Look for non-test, buildable .go files which could provide exports. - all, err := ioutil.ReadDir(dir) + all, err := os.ReadDir(dir) if err != nil { return "", nil, err } - var files []os.FileInfo + var files []fs.DirEntry for _, fi := range all { name := fi.Name() if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) { diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index 1389d38b..5f4d435d 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -9,7 +9,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -38,7 +37,7 @@ type ModuleResolver struct { mains []*gocommand.ModuleJSON mainByDir map[string]*gocommand.ModuleJSON modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path... - modsByDir []*gocommand.ModuleJSON // ...or Dir. + modsByDir []*gocommand.ModuleJSON // ...or number of path components in their Dir. // moduleCacheCache stores information about the module cache. moduleCacheCache *dirInfoCache @@ -124,7 +123,7 @@ func (r *ModuleResolver) init() error { }) sort.Slice(r.modsByDir, func(i, j int) bool { count := func(x int) int { - return strings.Count(r.modsByDir[x].Dir, "/") + return strings.Count(r.modsByDir[x].Dir, string(filepath.Separator)) } return count(j) < count(i) // descending order }) @@ -265,7 +264,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, } // Not cached. Read the filesystem. - pkgFiles, err := ioutil.ReadDir(pkgDir) + pkgFiles, err := os.ReadDir(pkgDir) if err != nil { continue } @@ -328,6 +327,10 @@ func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON { // - in /vendor/ in -mod=vendor mode. // - nested module? Dunno. // Rumor has it that replace targets cannot contain other replace targets. + // + // Note that it is critical here that modsByDir is sorted to have deeper dirs + // first. This ensures that findModuleByDir finds the innermost module. + // See also golang/go#56291. for _, m := range r.modsByDir { if !strings.HasPrefix(dir, m.Dir) { continue @@ -366,7 +369,7 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { readModName := func(modFile string) string { - modBytes, err := ioutil.ReadFile(modFile) + modBytes, err := os.ReadFile(modFile) if err != nil { return "" } diff --git a/vendor/golang.org/x/tools/internal/imports/mod_cache.go b/vendor/golang.org/x/tools/internal/imports/mod_cache.go index 18dada49..45690abb 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod_cache.go +++ b/vendor/golang.org/x/tools/internal/imports/mod_cache.go @@ -12,7 +12,7 @@ import ( "golang.org/x/tools/internal/gopathwalk" ) -// To find packages to import, the resolver needs to know about all of the +// To find packages to import, the resolver needs to know about all of // the packages that could be imported. This includes packages that are // already in modules that are in (1) the current module, (2) replace targets, // and (3) packages in the module cache. Packages in (1) and (2) may change over diff --git a/vendor/golang.org/x/tools/internal/imports/zstdlib.go b/vendor/golang.org/x/tools/internal/imports/zstdlib.go index 31a75949..9f992c2b 100644 --- a/vendor/golang.org/x/tools/internal/imports/zstdlib.go +++ b/vendor/golang.org/x/tools/internal/imports/zstdlib.go @@ -93,6 +93,7 @@ var stdlib = map[string][]string{ "Compare", "Contains", "ContainsAny", + "ContainsFunc", "ContainsRune", "Count", "Cut", @@ -147,6 +148,11 @@ var stdlib = map[string][]string{ "TrimSpace", "TrimSuffix", }, + "cmp": { + "Compare", + "Less", + "Ordered", + }, "compress/bzip2": { "NewReader", "StructuralError", @@ -228,6 +234,7 @@ var stdlib = map[string][]string{ "Ring", }, "context": { + "AfterFunc", "Background", "CancelCauseFunc", "CancelFunc", @@ -239,8 +246,11 @@ var stdlib = map[string][]string{ "WithCancel", "WithCancelCause", "WithDeadline", + "WithDeadlineCause", "WithTimeout", + "WithTimeoutCause", "WithValue", + "WithoutCancel", }, "crypto": { "BLAKE2b_256", @@ -445,6 +455,7 @@ var stdlib = map[string][]string{ "XORBytes", }, "crypto/tls": { + "AlertError", "Certificate", "CertificateRequestInfo", "CertificateVerificationError", @@ -476,6 +487,7 @@ var stdlib = map[string][]string{ "LoadX509KeyPair", "NewLRUClientSessionCache", "NewListener", + "NewResumptionState", "NoClientCert", "PKCS1WithSHA1", "PKCS1WithSHA256", @@ -484,6 +496,27 @@ var stdlib = map[string][]string{ "PSSWithSHA256", "PSSWithSHA384", "PSSWithSHA512", + "ParseSessionState", + "QUICClient", + "QUICConfig", + "QUICConn", + "QUICEncryptionLevel", + "QUICEncryptionLevelApplication", + "QUICEncryptionLevelEarly", + "QUICEncryptionLevelHandshake", + "QUICEncryptionLevelInitial", + "QUICEvent", + "QUICEventKind", + "QUICHandshakeDone", + "QUICNoEvent", + "QUICRejectedEarlyData", + "QUICServer", + "QUICSessionTicketOptions", + "QUICSetReadSecret", + "QUICSetWriteSecret", + "QUICTransportParameters", + "QUICTransportParametersRequired", + "QUICWriteData", "RecordHeaderError", "RenegotiateFreelyAsClient", "RenegotiateNever", @@ -493,6 +526,7 @@ var stdlib = map[string][]string{ "RequireAndVerifyClientCert", "RequireAnyClientCert", "Server", + "SessionState", "SignatureScheme", "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", @@ -523,6 +557,7 @@ var stdlib = map[string][]string{ "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_RC4_128_SHA", "VerifyClientCertIfGiven", + "VersionName", "VersionSSL30", "VersionTLS10", "VersionTLS11", @@ -618,6 +653,7 @@ var stdlib = map[string][]string{ "PureEd25519", "RSA", "RevocationList", + "RevocationListEntry", "SHA1WithRSA", "SHA256WithRSA", "SHA256WithRSAPSS", @@ -1002,10 +1038,42 @@ var stdlib = map[string][]string{ "COMPRESS_LOOS", "COMPRESS_LOPROC", "COMPRESS_ZLIB", + "COMPRESS_ZSTD", "Chdr32", "Chdr64", "Class", "CompressionType", + "DF_1_CONFALT", + "DF_1_DIRECT", + "DF_1_DISPRELDNE", + "DF_1_DISPRELPND", + "DF_1_EDITED", + "DF_1_ENDFILTEE", + "DF_1_GLOBAL", + "DF_1_GLOBAUDIT", + "DF_1_GROUP", + "DF_1_IGNMULDEF", + "DF_1_INITFIRST", + "DF_1_INTERPOSE", + "DF_1_KMOD", + "DF_1_LOADFLTR", + "DF_1_NOCOMMON", + "DF_1_NODEFLIB", + "DF_1_NODELETE", + "DF_1_NODIRECT", + "DF_1_NODUMP", + "DF_1_NOHDR", + "DF_1_NOKSYMS", + "DF_1_NOOPEN", + "DF_1_NORELOC", + "DF_1_NOW", + "DF_1_ORIGIN", + "DF_1_PIE", + "DF_1_SINGLETON", + "DF_1_STUB", + "DF_1_SYMINTPOSE", + "DF_1_TRANS", + "DF_1_WEAKFILTER", "DF_BIND_NOW", "DF_ORIGIN", "DF_STATIC_TLS", @@ -1144,6 +1212,7 @@ var stdlib = map[string][]string{ "Dyn32", "Dyn64", "DynFlag", + "DynFlag1", "DynTag", "EI_ABIVERSION", "EI_CLASS", @@ -2111,6 +2180,7 @@ var stdlib = map[string][]string{ "R_PPC64_REL16_LO", "R_PPC64_REL24", "R_PPC64_REL24_NOTOC", + "R_PPC64_REL24_P9NOTOC", "R_PPC64_REL30", "R_PPC64_REL32", "R_PPC64_REL64", @@ -2848,6 +2918,7 @@ var stdlib = map[string][]string{ "MaxVarintLen16", "MaxVarintLen32", "MaxVarintLen64", + "NativeEndian", "PutUvarint", "PutVarint", "Read", @@ -2963,6 +3034,7 @@ var stdlib = map[string][]string{ }, "errors": { "As", + "ErrUnsupported", "Is", "Join", "New", @@ -2989,6 +3061,7 @@ var stdlib = map[string][]string{ "Arg", "Args", "Bool", + "BoolFunc", "BoolVar", "CommandLine", "ContinueOnError", @@ -3119,6 +3192,7 @@ var stdlib = map[string][]string{ "Inspect", "InterfaceType", "IsExported", + "IsGenerated", "KeyValueExpr", "LabeledStmt", "Lbl", @@ -3169,6 +3243,7 @@ var stdlib = map[string][]string{ "ArchChar", "Context", "Default", + "Directive", "FindOnly", "IgnoreVendor", "Import", @@ -3184,6 +3259,7 @@ var stdlib = map[string][]string{ "go/build/constraint": { "AndExpr", "Expr", + "GoVersion", "IsGoBuild", "IsPlusBuild", "NotExpr", @@ -3626,6 +3702,7 @@ var stdlib = map[string][]string{ "ErrBadHTML", "ErrBranchEnd", "ErrEndContext", + "ErrJSTemplate", "ErrNoSuchTemplate", "ErrOutputContext", "ErrPartialCharset", @@ -3870,6 +3947,8 @@ var stdlib = map[string][]string{ "FileInfo", "FileInfoToDirEntry", "FileMode", + "FormatDirEntry", + "FormatFileInfo", "Glob", "GlobFS", "ModeAppend", @@ -3942,6 +4021,78 @@ var stdlib = map[string][]string{ "SetPrefix", "Writer", }, + "log/slog": { + "Any", + "AnyValue", + "Attr", + "Bool", + "BoolValue", + "Debug", + "DebugContext", + "Default", + "Duration", + "DurationValue", + "Error", + "ErrorContext", + "Float64", + "Float64Value", + "Group", + "GroupValue", + "Handler", + "HandlerOptions", + "Info", + "InfoContext", + "Int", + "Int64", + "Int64Value", + "IntValue", + "JSONHandler", + "Kind", + "KindAny", + "KindBool", + "KindDuration", + "KindFloat64", + "KindGroup", + "KindInt64", + "KindLogValuer", + "KindString", + "KindTime", + "KindUint64", + "Level", + "LevelDebug", + "LevelError", + "LevelInfo", + "LevelKey", + "LevelVar", + "LevelWarn", + "Leveler", + "Log", + "LogAttrs", + "LogValuer", + "Logger", + "MessageKey", + "New", + "NewJSONHandler", + "NewLogLogger", + "NewRecord", + "NewTextHandler", + "Record", + "SetDefault", + "Source", + "SourceKey", + "String", + "StringValue", + "TextHandler", + "Time", + "TimeKey", + "TimeValue", + "Uint64", + "Uint64Value", + "Value", + "Warn", + "WarnContext", + "With", + }, "log/syslog": { "Dial", "LOG_ALERT", @@ -3977,6 +4128,13 @@ var stdlib = map[string][]string{ "Priority", "Writer", }, + "maps": { + "Clone", + "Copy", + "DeleteFunc", + "Equal", + "EqualFunc", + }, "math": { "Abs", "Acos", @@ -4371,6 +4529,7 @@ var stdlib = map[string][]string{ "ErrNoLocation", "ErrNotMultipart", "ErrNotSupported", + "ErrSchemeMismatch", "ErrServerClosed", "ErrShortBody", "ErrSkipAltProtocol", @@ -5084,6 +5243,8 @@ var stdlib = map[string][]string{ "NumCPU", "NumCgoCall", "NumGoroutine", + "PanicNilError", + "Pinner", "ReadMemStats", "ReadTrace", "SetBlockProfileRate", @@ -5172,6 +5333,37 @@ var stdlib = map[string][]string{ "Task", "WithRegion", }, + "slices": { + "BinarySearch", + "BinarySearchFunc", + "Clip", + "Clone", + "Compact", + "CompactFunc", + "Compare", + "CompareFunc", + "Contains", + "ContainsFunc", + "Delete", + "DeleteFunc", + "Equal", + "EqualFunc", + "Grow", + "Index", + "IndexFunc", + "Insert", + "IsSorted", + "IsSortedFunc", + "Max", + "MaxFunc", + "Min", + "MinFunc", + "Replace", + "Reverse", + "Sort", + "SortFunc", + "SortStableFunc", + }, "sort": { "Find", "Float64Slice", @@ -5242,6 +5434,7 @@ var stdlib = map[string][]string{ "Compare", "Contains", "ContainsAny", + "ContainsFunc", "ContainsRune", "Count", "Cut", @@ -5299,6 +5492,9 @@ var stdlib = map[string][]string{ "Mutex", "NewCond", "Once", + "OnceFunc", + "OnceValue", + "OnceValues", "Pool", "RWMutex", "WaitGroup", @@ -9135,10 +9331,12 @@ var stdlib = map[string][]string{ "SYS_AIO_CANCEL", "SYS_AIO_ERROR", "SYS_AIO_FSYNC", + "SYS_AIO_MLOCK", "SYS_AIO_READ", "SYS_AIO_RETURN", "SYS_AIO_SUSPEND", "SYS_AIO_SUSPEND_NOCANCEL", + "SYS_AIO_WAITCOMPLETE", "SYS_AIO_WRITE", "SYS_ALARM", "SYS_ARCH_PRCTL", @@ -9368,6 +9566,7 @@ var stdlib = map[string][]string{ "SYS_GET_MEMPOLICY", "SYS_GET_ROBUST_LIST", "SYS_GET_THREAD_AREA", + "SYS_GSSD_SYSCALL", "SYS_GTTY", "SYS_IDENTITYSVC", "SYS_IDLE", @@ -9411,8 +9610,24 @@ var stdlib = map[string][]string{ "SYS_KLDSYM", "SYS_KLDUNLOAD", "SYS_KLDUNLOADF", + "SYS_KMQ_NOTIFY", + "SYS_KMQ_OPEN", + "SYS_KMQ_SETATTR", + "SYS_KMQ_TIMEDRECEIVE", + "SYS_KMQ_TIMEDSEND", + "SYS_KMQ_UNLINK", "SYS_KQUEUE", "SYS_KQUEUE1", + "SYS_KSEM_CLOSE", + "SYS_KSEM_DESTROY", + "SYS_KSEM_GETVALUE", + "SYS_KSEM_INIT", + "SYS_KSEM_OPEN", + "SYS_KSEM_POST", + "SYS_KSEM_TIMEDWAIT", + "SYS_KSEM_TRYWAIT", + "SYS_KSEM_UNLINK", + "SYS_KSEM_WAIT", "SYS_KTIMER_CREATE", "SYS_KTIMER_DELETE", "SYS_KTIMER_GETOVERRUN", @@ -9504,11 +9719,14 @@ var stdlib = map[string][]string{ "SYS_NFSSVC", "SYS_NFSTAT", "SYS_NICE", + "SYS_NLM_SYSCALL", "SYS_NLSTAT", "SYS_NMOUNT", "SYS_NSTAT", "SYS_NTP_ADJTIME", "SYS_NTP_GETTIME", + "SYS_NUMA_GETAFFINITY", + "SYS_NUMA_SETAFFINITY", "SYS_OABI_SYSCALL_BASE", "SYS_OBREAK", "SYS_OLDFSTAT", @@ -9891,6 +10109,7 @@ var stdlib = map[string][]string{ "SYS___ACL_SET_FD", "SYS___ACL_SET_FILE", "SYS___ACL_SET_LINK", + "SYS___CAP_RIGHTS_GET", "SYS___CLONE", "SYS___DISABLE_THREADSIGNAL", "SYS___GETCWD", @@ -10574,6 +10793,7 @@ var stdlib = map[string][]string{ "Short", "T", "TB", + "Testing", "Verbose", }, "testing/fstest": { @@ -10603,6 +10823,9 @@ var stdlib = map[string][]string{ "SetupError", "Value", }, + "testing/slogtest": { + "TestHandler", + }, "text/scanner": { "Char", "Comment", @@ -10826,6 +11049,7 @@ var stdlib = map[string][]string{ "Cs", "Cuneiform", "Cypriot", + "Cypro_Minoan", "Cyrillic", "Dash", "Deprecated", @@ -10889,6 +11113,7 @@ var stdlib = map[string][]string{ "Kaithi", "Kannada", "Katakana", + "Kawi", "Kayah_Li", "Kharoshthi", "Khitan_Small_Script", @@ -10943,6 +11168,7 @@ var stdlib = map[string][]string{ "Myanmar", "N", "Nabataean", + "Nag_Mundari", "Nandinagari", "Nd", "New_Tai_Lue", @@ -10964,6 +11190,7 @@ var stdlib = map[string][]string{ "Old_Sogdian", "Old_South_Arabian", "Old_Turkic", + "Old_Uyghur", "Oriya", "Osage", "Osmanya", @@ -11038,6 +11265,7 @@ var stdlib = map[string][]string{ "Tai_Viet", "Takri", "Tamil", + "Tangsa", "Tangut", "Telugu", "Terminal_Punctuation", @@ -11052,6 +11280,7 @@ var stdlib = map[string][]string{ "ToLower", "ToTitle", "ToUpper", + "Toto", "TurkishCase", "Ugaritic", "Unified_Ideograph", @@ -11061,6 +11290,7 @@ var stdlib = map[string][]string{ "Vai", "Variation_Selector", "Version", + "Vithkuqi", "Wancho", "Warang_Citi", "White_Space", diff --git a/vendor/golang.org/x/tools/internal/typeparams/common.go b/vendor/golang.org/x/tools/internal/typeparams/common.go index cfba8189..d0d0649f 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/common.go +++ b/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -23,6 +23,7 @@ package typeparams import ( + "fmt" "go/ast" "go/token" "go/types" @@ -105,6 +106,31 @@ func OriginMethod(fn *types.Func) *types.Func { } orig := NamedTypeOrigin(named) gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) + + // This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In: + // package p + // type T *int + // func (*T) f() {} + // LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}. + // Here we make them consistent by force. + // (The go/types bug is general, but this workaround is reached only + // for generic T thanks to the early return above.) + if gfn == nil { + mset := types.NewMethodSet(types.NewPointer(orig)) + for i := 0; i < mset.Len(); i++ { + m := mset.At(i) + if m.Obj().Id() == fn.Id() { + gfn = m.Obj() + break + } + } + } + + // In golang/go#61196, we observe another crash, this time inexplicable. + if gfn == nil { + panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods())) + } + return gfn.(*types.Func) } diff --git a/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/vendor/golang.org/x/tools/internal/typeparams/coretype.go index 993135ec..71248209 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/coretype.go +++ b/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -81,13 +81,13 @@ func CoreType(T types.Type) types.Type { // restrictions may be arbitrarily complex. For example, consider the // following: // -// type A interface{ ~string|~[]byte } +// type A interface{ ~string|~[]byte } // -// type B interface{ int|string } +// type B interface{ int|string } // -// type C interface { ~string|~int } +// type C interface { ~string|~int } // -// type T[P interface{ A|B; C }] int +// type T[P interface{ A|B; C }] int // // In this example, the structural type restriction of P is ~string|int: A|B // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, diff --git a/vendor/golang.org/x/tools/internal/typeparams/termlist.go b/vendor/golang.org/x/tools/internal/typeparams/termlist.go index 933106a2..cbd12f80 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/termlist.go +++ b/vendor/golang.org/x/tools/internal/typeparams/termlist.go @@ -30,7 +30,7 @@ func (xl termlist) String() string { var buf bytes.Buffer for i, x := range xl { if i > 0 { - buf.WriteString(" ∪ ") + buf.WriteString(" | ") } buf.WriteString(x.String()) } diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go index b4788978..7ed86e17 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go +++ b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go @@ -129,7 +129,7 @@ func NamedTypeArgs(*types.Named) *TypeList { } // NamedTypeOrigin is the identity method at this Go version. -func NamedTypeOrigin(named *types.Named) types.Type { +func NamedTypeOrigin(named *types.Named) *types.Named { return named } diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go index 114a36b8..cf301af1 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go +++ b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go @@ -103,7 +103,7 @@ func NamedTypeArgs(named *types.Named) *TypeList { } // NamedTypeOrigin returns named.Orig(). -func NamedTypeOrigin(named *types.Named) types.Type { +func NamedTypeOrigin(named *types.Named) *types.Named { return named.Origin() } diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go index 7ddee28d..7350bb70 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go +++ b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go @@ -10,11 +10,10 @@ import "go/types" // A term describes elementary type sets: // -// ∅: (*term)(nil) == ∅ // set of no types (empty set) -// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) -// T: &term{false, T} == {T} // set of type T -// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t -// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t type term struct { tilde bool // valid if typ != nil typ types.Type diff --git a/vendor/modules.txt b/vendor/modules.txt index f61c54f9..31aec320 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -17,6 +17,10 @@ github.com/Microsoft/go-winio/pkg/guid # github.com/NYTimes/gziphandler v1.1.1 ## explicit; go 1.11 github.com/NYTimes/gziphandler +# github.com/agiledragon/gomonkey/v2 v2.11.0 +## explicit; go 1.14 +github.com/agiledragon/gomonkey/v2 +github.com/agiledragon/gomonkey/v2/creflect # github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 ## explicit; go 1.16 github.com/antlr/antlr4/runtime/Go/antlr @@ -184,6 +188,9 @@ github.com/gogo/protobuf/sortkeys # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da ## explicit github.com/golang/groupcache/lru +# github.com/golang/mock v1.6.0 +## explicit; go 1.11 +github.com/golang/mock/gomock # github.com/golang/protobuf v1.5.3 ## explicit; go 1.9 github.com/golang/protobuf/jsonpb @@ -616,8 +623,8 @@ go.uber.org/zap/internal/exit go.uber.org/zap/internal/pool go.uber.org/zap/zapcore go.uber.org/zap/zapgrpc -# golang.org/x/crypto v0.14.0 -## explicit; go 1.17 +# golang.org/x/crypto v0.15.0 +## explicit; go 1.18 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish golang.org/x/crypto/cryptobyte @@ -628,13 +635,13 @@ golang.org/x/crypto/nacl/secretbox golang.org/x/crypto/pbkdf2 golang.org/x/crypto/salsa20/salsa golang.org/x/crypto/scrypt -# golang.org/x/mod v0.10.0 -## explicit; go 1.17 +# golang.org/x/mod v0.14.0 +## explicit; go 1.18 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.17.0 -## explicit; go 1.17 +# golang.org/x/net v0.18.0 +## explicit; go 1.18 golang.org/x/net/bpf golang.org/x/net/context golang.org/x/net/html @@ -658,22 +665,22 @@ golang.org/x/net/websocket ## explicit; go 1.17 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.3.0 -## explicit; go 1.17 +# golang.org/x/sync v0.5.0 +## explicit; go 1.18 golang.org/x/sync/singleflight -# golang.org/x/sys v0.13.0 -## explicit; go 1.17 +# golang.org/x/sys v0.14.0 +## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/execabs golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry -# golang.org/x/term v0.13.0 -## explicit; go 1.17 +# golang.org/x/term v0.14.0 +## explicit; go 1.18 golang.org/x/term -# golang.org/x/text v0.13.0 -## explicit; go 1.17 +# golang.org/x/text v0.14.0 +## explicit; go 1.18 golang.org/x/text/encoding golang.org/x/text/encoding/charmap golang.org/x/text/encoding/htmlindex @@ -698,7 +705,7 @@ golang.org/x/text/width # golang.org/x/time v0.3.0 ## explicit golang.org/x/time/rate -# golang.org/x/tools v0.9.3 +# golang.org/x/tools v0.15.0 ## explicit; go 1.18 golang.org/x/tools/cmd/stringer golang.org/x/tools/go/ast/astutil @@ -709,13 +716,13 @@ golang.org/x/tools/go/internal/cgo golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/go/loader golang.org/x/tools/go/packages +golang.org/x/tools/go/types/objectpath golang.org/x/tools/imports golang.org/x/tools/internal/event golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/keys golang.org/x/tools/internal/event/label golang.org/x/tools/internal/event/tag -golang.org/x/tools/internal/fastwalk golang.org/x/tools/internal/gcimporter golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/gopathwalk @@ -1504,6 +1511,7 @@ sigs.k8s.io/controller-runtime/pkg/certwatcher/metrics sigs.k8s.io/controller-runtime/pkg/client sigs.k8s.io/controller-runtime/pkg/client/apiutil sigs.k8s.io/controller-runtime/pkg/client/config +sigs.k8s.io/controller-runtime/pkg/client/fake sigs.k8s.io/controller-runtime/pkg/cluster sigs.k8s.io/controller-runtime/pkg/config sigs.k8s.io/controller-runtime/pkg/config/v1alpha1 diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go new file mode 100644 index 00000000..4da64231 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go @@ -0,0 +1,933 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "sync" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + utilrand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/testing" + "sigs.k8s.io/controller-runtime/pkg/internal/field/selector" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/internal/objectutil" +) + +type versionedTracker struct { + testing.ObjectTracker + scheme *runtime.Scheme +} + +type fakeClient struct { + tracker versionedTracker + scheme *runtime.Scheme + restMapper meta.RESTMapper + + // indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK. + // The inner map maps from index name to IndexerFunc. + indexes map[schema.GroupVersionKind]map[string]client.IndexerFunc + + schemeWriteLock sync.Mutex +} + +var _ client.WithWatch = &fakeClient{} + +const ( + maxNameLength = 63 + randomLength = 5 + maxGeneratedNameLength = maxNameLength - randomLength +) + +// NewFakeClient creates a new fake client for testing. +// You can choose to initialize it with a slice of runtime.Object. +// +// Deprecated: Please use NewClientBuilder instead. +func NewFakeClient(initObjs ...runtime.Object) client.WithWatch { + return NewClientBuilder().WithRuntimeObjects(initObjs...).Build() +} + +// NewFakeClientWithScheme creates a new fake client with the given scheme +// for testing. +// You can choose to initialize it with a slice of runtime.Object. +// +// Deprecated: Please use NewClientBuilder instead. +func NewFakeClientWithScheme(clientScheme *runtime.Scheme, initObjs ...runtime.Object) client.WithWatch { + return NewClientBuilder().WithScheme(clientScheme).WithRuntimeObjects(initObjs...).Build() +} + +// NewClientBuilder returns a new builder to create a fake client. +func NewClientBuilder() *ClientBuilder { + return &ClientBuilder{} +} + +// ClientBuilder builds a fake client. +type ClientBuilder struct { + scheme *runtime.Scheme + restMapper meta.RESTMapper + initObject []client.Object + initLists []client.ObjectList + initRuntimeObjects []runtime.Object + objectTracker testing.ObjectTracker + + // indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK. + // The inner map maps from index name to IndexerFunc. + indexes map[schema.GroupVersionKind]map[string]client.IndexerFunc +} + +// WithScheme sets this builder's internal scheme. +// If not set, defaults to client-go's global scheme.Scheme. +func (f *ClientBuilder) WithScheme(scheme *runtime.Scheme) *ClientBuilder { + f.scheme = scheme + return f +} + +// WithRESTMapper sets this builder's restMapper. +// The restMapper is directly set as mapper in the Client. This can be used for example +// with a meta.DefaultRESTMapper to provide a static rest mapping. +// If not set, defaults to an empty meta.DefaultRESTMapper. +func (f *ClientBuilder) WithRESTMapper(restMapper meta.RESTMapper) *ClientBuilder { + f.restMapper = restMapper + return f +} + +// WithObjects can be optionally used to initialize this fake client with client.Object(s). +func (f *ClientBuilder) WithObjects(initObjs ...client.Object) *ClientBuilder { + f.initObject = append(f.initObject, initObjs...) + return f +} + +// WithLists can be optionally used to initialize this fake client with client.ObjectList(s). +func (f *ClientBuilder) WithLists(initLists ...client.ObjectList) *ClientBuilder { + f.initLists = append(f.initLists, initLists...) + return f +} + +// WithRuntimeObjects can be optionally used to initialize this fake client with runtime.Object(s). +func (f *ClientBuilder) WithRuntimeObjects(initRuntimeObjs ...runtime.Object) *ClientBuilder { + f.initRuntimeObjects = append(f.initRuntimeObjects, initRuntimeObjs...) + return f +} + +// WithObjectTracker can be optionally used to initialize this fake client with testing.ObjectTracker. +func (f *ClientBuilder) WithObjectTracker(ot testing.ObjectTracker) *ClientBuilder { + f.objectTracker = ot + return f +} + +// WithIndex can be optionally used to register an index with name `field` and indexer `extractValue` +// for API objects of the same GroupVersionKind (GVK) as `obj` in the fake client. +// It can be invoked multiple times, both with objects of the same GVK or different ones. +// Invoking WithIndex twice with the same `field` and GVK (via `obj`) arguments will panic. +// WithIndex retrieves the GVK of `obj` using the scheme registered via WithScheme if +// WithScheme was previously invoked, the default scheme otherwise. +func (f *ClientBuilder) WithIndex(obj runtime.Object, field string, extractValue client.IndexerFunc) *ClientBuilder { + objScheme := f.scheme + if objScheme == nil { + objScheme = scheme.Scheme + } + + gvk, err := apiutil.GVKForObject(obj, objScheme) + if err != nil { + panic(err) + } + + // If this is the first index being registered, we initialize the map storing all the indexes. + if f.indexes == nil { + f.indexes = make(map[schema.GroupVersionKind]map[string]client.IndexerFunc) + } + + // If this is the first index being registered for the GroupVersionKind of `obj`, we initialize + // the map storing the indexes for that GroupVersionKind. + if f.indexes[gvk] == nil { + f.indexes[gvk] = make(map[string]client.IndexerFunc) + } + + if _, fieldAlreadyIndexed := f.indexes[gvk][field]; fieldAlreadyIndexed { + panic(fmt.Errorf("indexer conflict: field %s for GroupVersionKind %v is already indexed", + field, gvk)) + } + + f.indexes[gvk][field] = extractValue + + return f +} + +// Build builds and returns a new fake client. +func (f *ClientBuilder) Build() client.WithWatch { + if f.scheme == nil { + f.scheme = scheme.Scheme + } + if f.restMapper == nil { + f.restMapper = meta.NewDefaultRESTMapper([]schema.GroupVersion{}) + } + + var tracker versionedTracker + + if f.objectTracker == nil { + tracker = versionedTracker{ObjectTracker: testing.NewObjectTracker(f.scheme, scheme.Codecs.UniversalDecoder()), scheme: f.scheme} + } else { + tracker = versionedTracker{ObjectTracker: f.objectTracker, scheme: f.scheme} + } + + for _, obj := range f.initObject { + if err := tracker.Add(obj); err != nil { + panic(fmt.Errorf("failed to add object %v to fake client: %w", obj, err)) + } + } + for _, obj := range f.initLists { + if err := tracker.Add(obj); err != nil { + panic(fmt.Errorf("failed to add list %v to fake client: %w", obj, err)) + } + } + for _, obj := range f.initRuntimeObjects { + if err := tracker.Add(obj); err != nil { + panic(fmt.Errorf("failed to add runtime object %v to fake client: %w", obj, err)) + } + } + return &fakeClient{ + tracker: tracker, + scheme: f.scheme, + restMapper: f.restMapper, + indexes: f.indexes, + } +} + +const trackerAddResourceVersion = "999" + +func (t versionedTracker) Add(obj runtime.Object) error { + var objects []runtime.Object + if meta.IsListType(obj) { + var err error + objects, err = meta.ExtractList(obj) + if err != nil { + return err + } + } else { + objects = []runtime.Object{obj} + } + for _, obj := range objects { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + if accessor.GetResourceVersion() == "" { + // We use a "magic" value of 999 here because this field + // is parsed as uint and and 0 is already used in Update. + // As we can't go lower, go very high instead so this can + // be recognized + accessor.SetResourceVersion(trackerAddResourceVersion) + } + + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + if err != nil { + return err + } + if err := t.ObjectTracker.Add(obj); err != nil { + return err + } + } + + return nil +} + +func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + if accessor.GetName() == "" { + return apierrors.NewInvalid( + obj.GetObjectKind().GroupVersionKind().GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + if accessor.GetResourceVersion() != "" { + return apierrors.NewBadRequest("resourceVersion can not be set for Create requests") + } + accessor.SetResourceVersion("1") + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + if err != nil { + return err + } + if err := t.ObjectTracker.Create(gvr, obj, ns); err != nil { + accessor.SetResourceVersion("") + return err + } + + return nil +} + +// convertFromUnstructuredIfNecessary will convert *unstructured.Unstructured for a GVK that is recocnized +// by the schema into the whatever the schema produces with New() for said GVK. +// This is required because the tracker unconditionally saves on manipulations, but its List() implementation +// tries to assign whatever it finds into a ListType it gets from schema.New() - Thus we have to ensure +// we save as the very same type, otherwise subsequent List requests will fail. +func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (runtime.Object, error) { + u, isUnstructured := o.(*unstructured.Unstructured) + if !isUnstructured || !s.Recognizes(u.GroupVersionKind()) { + return o, nil + } + + typed, err := s.New(u.GroupVersionKind()) + if err != nil { + return nil, fmt.Errorf("scheme recognizes %s but failed to produce an object for it: %w", u.GroupVersionKind().String(), err) + } + + unstructuredSerialized, err := json.Marshal(u) + if err != nil { + return nil, fmt.Errorf("failed to serialize %T: %w", unstructuredSerialized, err) + } + if err := json.Unmarshal(unstructuredSerialized, typed); err != nil { + return nil, fmt.Errorf("failed to unmarshal the content of %T into %T: %w", u, typed, err) + } + + return typed, nil +} + +func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + + if accessor.GetName() == "" { + return apierrors.NewInvalid( + obj.GetObjectKind().GroupVersionKind().GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + + gvk := obj.GetObjectKind().GroupVersionKind() + if gvk.Empty() { + gvk, err = apiutil.GVKForObject(obj, t.scheme) + if err != nil { + return err + } + } + + oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName()) + if err != nil { + // If the resource is not found and the resource allows create on update, issue a + // create instead. + if apierrors.IsNotFound(err) && allowsCreateOnUpdate(gvk) { + return t.Create(gvr, obj, ns) + } + return err + } + + oldAccessor, err := meta.Accessor(oldObject) + if err != nil { + return err + } + + // If the new object does not have the resource version set and it allows unconditional update, + // default it to the resource version of the existing resource + if accessor.GetResourceVersion() == "" && allowsUnconditionalUpdate(gvk) { + accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) + } + if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() { + return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified")) + } + if oldAccessor.GetResourceVersion() == "" { + oldAccessor.SetResourceVersion("0") + } + intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64) + if err != nil { + return fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err) + } + intResourceVersion++ + accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10)) + if !accessor.GetDeletionTimestamp().IsZero() && len(accessor.GetFinalizers()) == 0 { + return t.ObjectTracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName()) + } + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + if err != nil { + return err + } + return t.ObjectTracker.Update(gvr, obj, ns) +} + +func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + o, err := c.tracker.Get(gvr, key.Namespace, key.Name) + if err != nil { + return err + } + + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(gvk.Kind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + zero(obj) + _, _, err = decoder.Decode(j, nil, obj) + return err +} + +func (c *fakeClient) Watch(ctx context.Context, list client.ObjectList, opts ...client.ListOption) (watch.Interface, error) { + gvk, err := apiutil.GVKForObject(list, c.scheme) + if err != nil { + return nil, err + } + + gvk.Kind = strings.TrimSuffix(gvk.Kind, "List") + + listOpts := client.ListOptions{} + listOpts.ApplyOptions(opts) + + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + return c.tracker.Watch(gvr, listOpts.Namespace) +} + +func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error { + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + + originalKind := gvk.Kind + + gvk.Kind = strings.TrimSuffix(gvk.Kind, "List") + + if _, isUnstructuredList := obj.(*unstructured.UnstructuredList); isUnstructuredList && !c.scheme.Recognizes(gvk) { + // We need to register the ListKind with UnstructuredList: + // https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51 + c.schemeWriteLock.Lock() + c.scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(gvk.Kind+"List"), &unstructured.UnstructuredList{}) + c.schemeWriteLock.Unlock() + } + + listOpts := client.ListOptions{} + listOpts.ApplyOptions(opts) + + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + o, err := c.tracker.List(gvr, gvk, listOpts.Namespace) + if err != nil { + return err + } + + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(originalKind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + zero(obj) + _, _, err = decoder.Decode(j, nil, obj) + if err != nil { + return err + } + + if listOpts.LabelSelector == nil && listOpts.FieldSelector == nil { + return nil + } + + // If we're here, either a label or field selector are specified (or both), so before we return + // the list we must filter it. If both selectors are set, they are ANDed. + objs, err := meta.ExtractList(obj) + if err != nil { + return err + } + + filteredList, err := c.filterList(objs, gvk, listOpts.LabelSelector, listOpts.FieldSelector) + if err != nil { + return err + } + + return meta.SetList(obj, filteredList) +} + +func (c *fakeClient) filterList(list []runtime.Object, gvk schema.GroupVersionKind, ls labels.Selector, fs fields.Selector) ([]runtime.Object, error) { + // Filter the objects with the label selector + filteredList := list + if ls != nil { + objsFilteredByLabel, err := objectutil.FilterWithLabels(list, ls) + if err != nil { + return nil, err + } + filteredList = objsFilteredByLabel + } + + // Filter the result of the previous pass with the field selector + if fs != nil { + objsFilteredByField, err := c.filterWithFields(filteredList, gvk, fs) + if err != nil { + return nil, err + } + filteredList = objsFilteredByField + } + + return filteredList, nil +} + +func (c *fakeClient) filterWithFields(list []runtime.Object, gvk schema.GroupVersionKind, fs fields.Selector) ([]runtime.Object, error) { + // We only allow filtering on the basis of a single field to ensure consistency with the + // behavior of the cache reader (which we're faking here). + fieldKey, fieldVal, requiresExact := selector.RequiresExactMatch(fs) + if !requiresExact { + return nil, fmt.Errorf("field selector %s is not in one of the two supported forms \"key==val\" or \"key=val\"", + fs) + } + + // Field selection is mimicked via indexes, so there's no sane answer this function can give + // if there are no indexes registered for the GroupVersionKind of the objects in the list. + indexes := c.indexes[gvk] + if len(indexes) == 0 || indexes[fieldKey] == nil { + return nil, fmt.Errorf("List on GroupVersionKind %v specifies selector on field %s, but no "+ + "index with name %s has been registered for GroupVersionKind %v", gvk, fieldKey, fieldKey, gvk) + } + + indexExtractor := indexes[fieldKey] + filteredList := make([]runtime.Object, 0, len(list)) + for _, obj := range list { + if c.objMatchesFieldSelector(obj, indexExtractor, fieldVal) { + filteredList = append(filteredList, obj) + } + } + return filteredList, nil +} + +func (c *fakeClient) objMatchesFieldSelector(o runtime.Object, extractIndex client.IndexerFunc, val string) bool { + obj, isClientObject := o.(client.Object) + if !isClientObject { + panic(fmt.Errorf("expected object %v to be of type client.Object, but it's not", o)) + } + + for _, extractedVal := range extractIndex(obj) { + if extractedVal == val { + return true + } + } + + return false +} + +func (c *fakeClient) Scheme() *runtime.Scheme { + return c.scheme +} + +func (c *fakeClient) RESTMapper() meta.RESTMapper { + return c.restMapper +} + +func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + createOptions := &client.CreateOptions{} + createOptions.ApplyOptions(opts) + + for _, dryRunOpt := range createOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + + if accessor.GetName() == "" && accessor.GetGenerateName() != "" { + base := accessor.GetGenerateName() + if len(base) > maxGeneratedNameLength { + base = base[:maxGeneratedNameLength] + } + accessor.SetName(fmt.Sprintf("%s%s", base, utilrand.String(randomLength))) + } + + return c.tracker.Create(gvr, obj, accessor.GetNamespace()) +} + +func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + delOptions := client.DeleteOptions{} + delOptions.ApplyOptions(opts) + + for _, dryRunOpt := range delOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + // Check the ResourceVersion if that Precondition was specified. + if delOptions.Preconditions != nil && delOptions.Preconditions.ResourceVersion != nil { + name := accessor.GetName() + dbObj, err := c.tracker.Get(gvr, accessor.GetNamespace(), name) + if err != nil { + return err + } + oldAccessor, err := meta.Accessor(dbObj) + if err != nil { + return err + } + actualRV := oldAccessor.GetResourceVersion() + expectRV := *delOptions.Preconditions.ResourceVersion + if actualRV != expectRV { + msg := fmt.Sprintf( + "the ResourceVersion in the precondition (%s) does not match the ResourceVersion in record (%s). "+ + "The object might have been modified", + expectRV, actualRV) + return apierrors.NewConflict(gvr.GroupResource(), name, errors.New(msg)) + } + } + + return c.deleteObject(gvr, accessor) +} + +func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + + dcOptions := client.DeleteAllOfOptions{} + dcOptions.ApplyOptions(opts) + + for _, dryRunOpt := range dcOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + o, err := c.tracker.List(gvr, gvk, dcOptions.Namespace) + if err != nil { + return err + } + + objs, err := meta.ExtractList(o) + if err != nil { + return err + } + filteredObjs, err := objectutil.FilterWithLabels(objs, dcOptions.LabelSelector) + if err != nil { + return err + } + for _, o := range filteredObjs { + accessor, err := meta.Accessor(o) + if err != nil { + return err + } + err = c.deleteObject(gvr, accessor) + if err != nil { + return err + } + } + return nil +} + +func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + updateOptions := &client.UpdateOptions{} + updateOptions.ApplyOptions(opts) + + for _, dryRunOpt := range updateOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + return c.tracker.Update(gvr, obj, accessor.GetNamespace()) +} + +func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + patchOptions := &client.PatchOptions{} + patchOptions.ApplyOptions(opts) + + for _, dryRunOpt := range patchOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + data, err := patch.Data(obj) + if err != nil { + return err + } + + reaction := testing.ObjectReaction(c.tracker) + handled, o, err := reaction(testing.NewPatchAction(gvr, accessor.GetNamespace(), accessor.GetName(), patch.Type(), data)) + if err != nil { + return err + } + if !handled { + panic("tracker could not handle patch method") + } + + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(gvk.Kind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + zero(obj) + _, _, err = decoder.Decode(j, nil, obj) + return err +} + +func (c *fakeClient) Status() client.SubResourceWriter { + return c.SubResource("status") +} + +func (c *fakeClient) SubResource(subResource string) client.SubResourceClient { + return &fakeSubResourceClient{client: c} +} + +func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor metav1.Object) error { + old, err := c.tracker.Get(gvr, accessor.GetNamespace(), accessor.GetName()) + if err == nil { + oldAccessor, err := meta.Accessor(old) + if err == nil { + if len(oldAccessor.GetFinalizers()) > 0 { + now := metav1.Now() + oldAccessor.SetDeletionTimestamp(&now) + return c.tracker.Update(gvr, old, accessor.GetNamespace()) + } + } + } + + //TODO: implement propagation + return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName()) +} + +func getGVRFromObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionResource, error) { + gvk, err := apiutil.GVKForObject(obj, scheme) + if err != nil { + return schema.GroupVersionResource{}, err + } + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + return gvr, nil +} + +type fakeSubResourceClient struct { + client *fakeClient +} + +func (sw *fakeSubResourceClient) Get(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error { + panic("fakeSubResourceClient does not support get") +} + +func (sw *fakeSubResourceClient) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error { + panic("fakeSubResourceWriter does not support create") +} + +func (sw *fakeSubResourceClient) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + // TODO(droot): This results in full update of the obj (spec + subresources). Need + // a way to update subresource only. + updateOptions := client.SubResourceUpdateOptions{} + updateOptions.ApplyOptions(opts) + + body := obj + if updateOptions.SubResourceBody != nil { + body = updateOptions.SubResourceBody + } + return sw.client.Update(ctx, body, &updateOptions.UpdateOptions) +} + +func (sw *fakeSubResourceClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + // TODO(droot): This results in full update of the obj (spec + subresources). Need + // a way to update subresource only. + + patchOptions := client.SubResourcePatchOptions{} + patchOptions.ApplyOptions(opts) + + body := obj + if patchOptions.SubResourceBody != nil { + body = patchOptions.SubResourceBody + } + + return sw.client.Patch(ctx, body, patch, &patchOptions.PatchOptions) +} + +func allowsUnconditionalUpdate(gvk schema.GroupVersionKind) bool { + switch gvk.Group { + case "apps": + switch gvk.Kind { + case "ControllerRevision", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet": + return true + } + case "autoscaling": + switch gvk.Kind { + case "HorizontalPodAutoscaler": + return true + } + case "batch": + switch gvk.Kind { + case "CronJob", "Job": + return true + } + case "certificates": + switch gvk.Kind { + case "Certificates": + return true + } + case "flowcontrol": + switch gvk.Kind { + case "FlowSchema", "PriorityLevelConfiguration": + return true + } + case "networking": + switch gvk.Kind { + case "Ingress", "IngressClass", "NetworkPolicy": + return true + } + case "policy": + switch gvk.Kind { + case "PodSecurityPolicy": + return true + } + case "rbac": + switch gvk.Kind { + case "ClusterRole", "ClusterRoleBinding", "Role", "RoleBinding": + return true + } + case "scheduling": + switch gvk.Kind { + case "PriorityClass": + return true + } + case "settings": + switch gvk.Kind { + case "PodPreset": + return true + } + case "storage": + switch gvk.Kind { + case "StorageClass": + return true + } + case "": + switch gvk.Kind { + case "ConfigMap", "Endpoint", "Event", "LimitRange", "Namespace", "Node", + "PersistentVolume", "PersistentVolumeClaim", "Pod", "PodTemplate", + "ReplicationController", "ResourceQuota", "Secret", "Service", + "ServiceAccount", "EndpointSlice": + return true + } + } + + return false +} + +func allowsCreateOnUpdate(gvk schema.GroupVersionKind) bool { + switch gvk.Group { + case "coordination": + switch gvk.Kind { + case "Lease": + return true + } + case "node": + switch gvk.Kind { + case "RuntimeClass": + return true + } + case "rbac": + switch gvk.Kind { + case "ClusterRole", "ClusterRoleBinding", "Role", "RoleBinding": + return true + } + case "": + switch gvk.Kind { + case "Endpoint", "Event", "LimitRange", "Service": + return true + } + } + + return false +} + +// zero zeros the value of a pointer. +func zero(x interface{}) { + if x == nil { + return + } + res := reflect.ValueOf(x).Elem() + res.Set(reflect.Zero(res.Type())) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go new file mode 100644 index 00000000..d0614666 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Package fake provides a fake client for testing. + +A fake client is backed by its simple object store indexed by GroupVersionResource. +You can create a fake client with optional objects. + + client := NewFakeClientWithScheme(scheme, initObjs...) // initObjs is a slice of runtime.Object + +You can invoke the methods defined in the Client interface. + +When in doubt, it's almost always better not to use this package and instead use +envtest.Environment with a real client and API server. + +WARNING: ⚠️ Current Limitations / Known Issues with the fake Client ⚠️ + - This client does not have a way to inject specific errors to test handled vs. unhandled errors. + - There is some support for sub resources which can cause issues with tests if you're trying to update + e.g. metadata and status in the same reconcile. + - No OpenAPI validation is performed when creating or updating objects. + - ObjectMeta's `Generation` and `ResourceVersion` don't behave properly, Patch or Update + operations that rely on these fields will fail, or give false positives. +*/ +package fake From 12fb783bfaa5ab6fcc65730b20131dd76fd37a63 Mon Sep 17 00:00:00 2001 From: ii2day Date: Mon, 20 Nov 2023 19:43:41 +0800 Subject: [PATCH 14/41] fix http client timeout param error Signed-off-by: ii2day --- pkg/loadRequest/loadHttp/http_requester.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 3317525b..f4b38453 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -233,12 +233,7 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { var size int64 var dnsStart, connStart, resStart time.Duration var dnsDuration, connDuration, resDuration, reqDuration time.Duration - var req *http.Request - if b.RequestFunc != nil { - req = b.RequestFunc() - } else { - req = cloneRequest(b.Request, b.RequestBody) - } + req := genRequest(b.Request, b.RequestBody) trace := &httptrace.ClientTrace{ DNSStart: func(info httptrace.DNSStartInfo) { dnsStart = b.now() @@ -315,7 +310,7 @@ func (b *Work) runWorker() { tr.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper) } // Each goroutine uses the same HTTP Client instance - client := &http.Client{Transport: tr, Timeout: time.Duration(b.Timeout) * time.Second} + client := &http.Client{Transport: tr, Timeout: time.Duration(b.Timeout) * time.Millisecond} if b.DisableRedirects { client.CheckRedirect = func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse @@ -404,12 +399,9 @@ func (b *Work) AggregateMetric() *v1beta1.HttpMetrics { return metric } -// cloneRequest returns a clone of the provided *http.Request. -// The clone is a shallow copy of the struct and its Header map. -func cloneRequest(r *http.Request, body []byte) *http.Request { +func genRequest(r *http.Request, body []byte) *http.Request { // shallow copy of the struct - r2 := new(http.Request) - *r2 = *r + r2, _ := http.NewRequest(r.Method, r.URL.String(), nil) // deep copy of the Header r2.Header = make(http.Header, len(r.Header)) for k, s := range r.Header { From 6ed240fd146d2d3702cd77c0b7f4aa5148af18bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:05:12 +0000 Subject: [PATCH 15/41] Bump docker/build-push-action from 5.0.0 to 5.1.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.0.0...v5.1.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build-image-base.yaml | 2 +- .github/workflows/call-release-image.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-image-base.yaml b/.github/workflows/build-image-base.yaml index cd87bd3c..ac23c180 100644 --- a/.github/workflows/build-image-base.yaml +++ b/.github/workflows/build-image-base.yaml @@ -125,7 +125,7 @@ jobs: - name: Release build ${{ matrix.name }} if: ${{ env.RUN_EXIST == 'false' }} - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 continue-on-error: false id: docker_build_release with: diff --git a/.github/workflows/call-release-image.yaml b/.github/workflows/call-release-image.yaml index a594526c..775c8a20 100644 --- a/.github/workflows/call-release-image.yaml +++ b/.github/workflows/call-release-image.yaml @@ -110,7 +110,7 @@ jobs: echo "RUN_IMAGE_SUFFIX=${tmp}-${{ matrix.name }}" >> $GITHUB_ENV - name: Build Image ${{ matrix.name }} and push - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 if: ${{ env.RUN_PUSH == 'true' }} id: docker_build_and_push with: @@ -128,7 +128,7 @@ jobs: RACE=${{ inputs.race }} - name: Build Image ${{ matrix.name }} and output docker - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 if: ${{ env.RUN_PUSH != 'true' }} id: docker_build_and_save with: From 9aa5bce6e8a0fae417a77a3c41c6d79055d7029a Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 21 Nov 2023 14:46:23 +0800 Subject: [PATCH 16/41] add doc Signed-off-by: ii2day --- docs/README-zh_CN.md | 45 ++++++++++++++++++++++++------------- docs/README.md | 47 ++++++++++++++++++++++++--------------- docs/usage/debug-zh_CN.md | 37 ++++++++++++++++++++++++++++++ docs/usage/debug.md | 41 ++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 docs/usage/debug-zh_CN.md create mode 100644 docs/usage/debug.md diff --git a/docs/README-zh_CN.md b/docs/README-zh_CN.md index fcc3ca9c..10c2e31c 100644 --- a/docs/README-zh_CN.md +++ b/docs/README-zh_CN.md @@ -10,32 +10,47 @@ **简体中文** | [**English**](./README.md) -## Introduction +## 介绍 -kdoctor 是一个 kubernetes 数据面测试项目,通过压力注入的方式,实现对集群进行功能、性能的主动式巡检。 +kdoctor 是一个基于主动式压力注入的 Kubernetes 数据面测试组件,对集群进行功能、性能的测试。通过调研和抽象了运维人员的常规运维需求,让网络、存储、应用等运维任务进行了云原生实现,基于 CRD的设计,能够对接观测性组件。 -传统的集群巡检,通过采集指标、日志、应用状态等信息来确认集群和应用的状态,实现被动式巡检。但是在一些特殊场景下,这种方式可能不能实现预期的巡检目的、时效性、集群范围,运维人员就需要采用手动方式给集群注入一些压力,进行主动式巡检,当集群规模很大、巡检频率高或巡检流程复杂时,手工方式难以持久实施。这些场景包括: +**kdoctor 主要包含以下 3 个类型的任务:** +* [AppHttpHealthy](./reference/apphttphealthy-zh_CN.md): 根据任务配置对集群内外指定访问地址,使用 HTTP、HTTPS 协议进行连通性检查,支持 PUT、GET、POST 等多种请求方式。 +* [NetReach](./reference/netreach-zh_CN.md): 根据任务配置对集群内 Pod IP、ClusterIP、NodePort、Loadbalancer IP、Ingress IP, 甚至是 POD 多网卡、双栈IP进行连通性巡检。 +* [NetDns](./reference/netdns-zh_CN.md): 根据任务配置,对集群内外的指定 DNS Server 进行连通性检测,支持 udp、tcp、tcp-tls 协议。 -* 部署大规模集群后,希望确认所有节点间 POD 的网络连通性,避免某个节点存在网络故障,发现网络中是否存在偶发丢包问题,而通信渠道非常多,包括 pod IP、clusterIP、nodePort、loadbalancer ip、ingress ip, 甚至是 POD 多网卡、双栈IP +**kdoctor 较传统的测试组件有哪些优势:** +* 通过下发 CRD 配置巡检任务需求,使用者只需要关注巡检目标、巡检频率、发压参数以及期望巡检结果。 +* 通过读取任务配置,以 Deployment 或 DaemonSet 的方式运行发压 agent,以达到多台发压机器的效果。 +* 根据任务的 spec 配置,使用 default agent 或创建新的 agent 执行任务,以达到资源重复利用和任务资源隔离。 +* 绑定相对应的资源目标,如 ingress 、service,每一个 agent pod 根据任务配置相互访问绑定的资源,根据请求结果得出结论。 +* 发压 client 通过性能调优,大大降低了发压请求时的资源消耗。 +* 巡检报告通过日志、聚合 api 、文件落盘等方式输出。 -* 希望主动检测所有节点间上的 POD 能够正常访问 coredns 服务,希望确认 coredns 服务的资源配置和副本数量正确,其服务性能能欧支持预期的最大访问量 +## 架构 -* 磁盘是易耗品,例如 etcd 等应用对磁盘性能是比较敏感的,在日常运维工作中,管理员希望周期地确认所有节点的本地磁盘是正常的,文件读写的吞吐量和延时是符合预期的 +
    + Your Image Description +
    -* 给某个服务主动注入压力,它可能是镜像仓库、mysql 或者 api-server,以配合 BUG 复现,或确认服务性能 +组件构成: +* kdoctor controller: 以 Deployment 形式常驻,实施 CR 监控,任务创建,任务报告汇聚等。 +* kdoctor agent: 以 Deployment 或 DaemonSet 形式按需动态创建,任务的执行者。 -kdoctor 是一个 kubernetes 数据面测试项目,来源于生产运维过程中的实践场景,通过压力注入的方式,实现对集群进行功能、性能的主动式巡检。 kdoctor 可以应用于: +## 快速开始 -* 生产环境的部署检查、日常运维等场景,能避免了人工巡检的工作负担。 +**安装** +* [安装 kdoctor](./usage/install-zh_CN.md) 或 [kind 快速开始](./usage/install-zh_CN.md) -* 能应用 E2E 测试、bug 复现、混沌测试等,减少编程工作。 +**开始任务** +* [开始任务 AppHttpHealthy](./usage/apphttphealthy-zh_CN.md) +* [开始任务 NetReach](./usage/netreach-zh_CN.md) +* [开始任务 NetDNS](./usage/netdns-zh_CN.md) -## 架构 - -## 快速开始 +## 参与开发 -## 核心功能 +可参考 [开发搭建文档](./develop/contributing.md). ## License -kdoctor is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for the full license text. +kdoctor is licensed under the Apache License, Version 2.0. See [LICENSE](../LICENSE) for the full license text. diff --git a/docs/README.md b/docs/README.md index c9002a65..443591bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,34 +12,45 @@ ## Introduction -kdoctor is a cloud native project of data plane test. Through the pressure injection, it realizes the active inspection for the function and performance of the cluster. +kdoctor is a Kubernetes data plane testing component that conducts functional and performance tests on clusters using proactive pressure injection. It addresses the operational needs of network, storage, and applications by adopting a cloud-native approach based on extensive research and abstraction. With its CRD design, kdoctor can seamlessly integrate with observability components. -For the traditional operation and maintenance , the status of clusters and applications is confirmed by collecting information such as metrics, logs, and application status, -which could be called passive inspection. However, in some special scenarios, this method may not meet the expected purpose, timeliness, or cluster range, -administrators need to manually inject some pressure into the cluster and checkout the cluster status, which could be called active inspection. -When the cluster scale is large, or the inspection frequency is high, or the inspection process is complicated, it is hard to implement manually. These scenarios include: +**kdoctor mainly offers three types of tasks:** +* [AppHttpHealthy](./reference/apphttphealthy.md): according to the task configuration, perform connectivity checks using HTTP and HTTPS protocols on specified addresses within or outside the cluster, supporting various request methods such as PUT, GET, and POST. +* [NetReach](./reference/netreach.md): conduct connectivity inspections on Pod IP, ClusterIP, NodePort, LoadBalancer IP, Ingress IP, and even Pods with multiple network interfaces or dual-stack IPs. +* [NetDns](./reference/netdns.md): perform connectivity checks on designated DNS servers within or outside the cluster, supporting UDP, TCP, and TCP-TLS protocols. -* After deploying a large-scale cluster, administrators want to confirm the network connectivity between all nodes, to find out network failures on a certain - node, or occasional packet loss. In addition, there are many communication ways including POD IP, clusterIP, nodePort, loadbalancerIP, ingress, or even POD multiple network interface, dual-stack IP. +**Advantages of kdoctor over traditional testing components:** +* By configuring inspection tasks through CRDs, users only need to focus on the inspection targets, frequency, pressure parameters, and expected results. +* Pressure-injecting agents are dynamically run as Deployments or DaemonSets, achieving the effect of multiple pressure-injecting machines. +* The execution of tasks utilizes default agents or newly created agents based on the task's spec configurations, enabling resource reuse and task resource isolation. +* Agents are bound to corresponding resource targets such as ingress and service. Each agent Pod mutually accesses the bound resources according to the task configuration, deriving conclusions from the request results. +* Through performance optimization, the pressure-injecting client significantly reduces resource consumption during requests. +* Inspection reports can be generated through various means, including logging, aggregated APIs, and file storage. -* It is desired to make sure that PODs on all nodes can access the coredns service, or the resource configuration and the replica number of the coredns are enough to support expected access pressure. +## Architecture -* Disks are consumables and applications like etcd are sensitive to disk performance. In daily maintenance, administrators want to periodically confirm that local disks performance of all nodes are normal. +
    + Your Image Description +
    -* Actively inject pressure on a service like registry, mysql or api-server, to cooperate with BUG reproduce, or to confirm service performance +Components: +* kdoctor agent: kdoctor controller: a persistent Deployment responsible for CR monitoring, task creation, and task report aggregation. +* kdoctor agent: dynamically created on-demand as Deployments or DaemonSets to execute tasks. -kdoctor is a cloud native project of data plane test, which is derived from practices of the production operation and maintenance. Through the pressure injection, it realizes the active inspection for the function and performance of the cluster. kdoctor can be applied to scenarios: +## Quick Start -* inspection after creating new cluster, daily operation and maintenance. +**Install** +* Refer to [Install kdoctor](./usage/install.md) 或 [kind Quick Start](./usage/get-started-kind.md) -* E2E testing, bug reproduction, chaos testing. +**Task Get Started** +* [AppHttpHealthy Get Started](./usage/apphttphealthy.md) +* [NetReach Get Started](./usage/netreach.md) +* [NetDNS Get Started](./usage/netdns.md) -## Architecture - -## Quick Start +## Contribution -## Feature +Refer to the [Contribution doc](./develop/contributing.md). ## License -kdoctor is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for the full license text. +kdoctor is licensed under the Apache License, Version 2.0. See [LICENSE](../LICENSE) for the full license text. diff --git a/docs/usage/debug-zh_CN.md b/docs/usage/debug-zh_CN.md new file mode 100644 index 00000000..086b6f23 --- /dev/null +++ b/docs/usage/debug-zh_CN.md @@ -0,0 +1,37 @@ +# Debug + +[**English**](./debug.md) | **简体中文** + +**Q: 想要使用更高的 QPS 应该如何设置?** +* A: 当 QPS 设置过大,会导致服务器资源占用过高,影响业务。为了防止在生产坏境出现误操作。kdoctor 在 webhook 中添加了 QPS 的检查。如果您想使用更高的 QPS +可通过参数设置 QPS 限制 `--set feature.nethttp_defaultRequest_MaxQps=1000`,也可以通过 kdoctor 的 configmap 中去更改 `nethttp_defaultRequest_MaxQps` , +并重启 kdoctor 的相关 pod 重新加载 configmap。 + +**Q: 为什么我的任务无法达到期望的 QPS ?** +* A:无法达到 QPS 的期望原因有很多主要分为以下几种原因: + * 并发 worker 设置过低,kdoctor 可通过设置参数调整并发数 `--set feature.nethttp_defaultConcurrency=50`,`--set feature.netdns_defaultConcurrency=50`。 + * kdoctor agent 分配资源不充足,可通过 kdoctor 的聚合报告`kubectl get kdoctorreport `查看任务消耗的 cpu 与 内存使用量,确定 kdoctor agent 资源分配是否充足。 + ```shell + ~kubectl get kdoctorreport test-task -oyaml + ... + SystemResource: + MaxCPU: 52.951% + MaxMemory: 120.00MB + MeanCPU: 32.645% + ... + ``` + * kdoctor agent 中是否在同时执行其他任务将资源占满。可通过 kdoctor 的聚合报告`kubectl get kdoctorreport 查看同时执行的其他任务 QPS 数量。 + 错开任务执行时间或通过定义 agentSpec 指定 kdoctor agent 执行任务将任务进行隔离。因 QPS 统计具有时效性,所以可搭配日志一起作为参考,在任务执行开始前,会将当前在执行的 QPS 输出到日志中。 + ```shell + ~kubectl logs kdoctor-agent-74rrp -n kdoctor |grep "Before the current task starts" + {"level":"DEBUG","ts":"2023-11-07T10:01:02.821Z","agent":"agent.agentController.AppHttpHealthyReconciler.AppHttpHealthy.test-task.round1","caller":"pluginManager/agentTools.go:90","msg":"Before the current task starts, the total QPS of the tasks being executed is AppHttpHealth=100,NetReach=0,NetDNS=0","AppHttpHealthy":"test-task"} + ``` + ```shell + ~kubectl get kdoctorreport test-task -oyaml + ... + TotalRunningLoad: + AppHttpHealthyQPS: 100 + NetDnsQPS: 50 + NetReachQPS: 0 + ... + ``` diff --git a/docs/usage/debug.md b/docs/usage/debug.md new file mode 100644 index 00000000..4e1f89de --- /dev/null +++ b/docs/usage/debug.md @@ -0,0 +1,41 @@ +# Debug + +[**简体中文**](./debug-zh_CN.md) | **English** + + +**Q: How to achieve higher QPS?** +* A: When the QPS setting is too high, it can result in excessive server resource utilization, impacting business operations. + To prevent accidental misconfiguration in production environments, kdoctor has added QPS checks in the webhook. +* If you wish to use a higher QPS, you can set the QPS limit using the parameter `--set feature.nethttp_defaultRequest_MaxQps=1000`,You can also modify it through the configmap in kdoctor `nethttp_defaultRequest_MaxQps`, + And restart the relevant pods of kdoctor to reload the configmap. + +**Q: Why is my task unable to achieve the desired QPS ?** +* A:There are several reasons why the expected QPS cannot be achieved, primarily categorized into the following reasons: + * The concurrency worker setting is too low. kdoctor can adjust the concurrency by setting the parameters `--set feature.nethttp_defaultConcurrency=50` and `--set feature.netdns_defaultConcurrency=50`. + * The kdoctor agent may have insufficient resource allocation. You can use the kdoctor aggregate report `kubectl get kdoctorreport` to check the CPU and memory usage of the task. This will help you determine if the resource allocation for the kdoctor agent is sufficient. + ```shell + ~kubectl get kdoctorreport test-task -oyaml + ... + SystemResource: + MaxCPU: 52.951% + MaxMemory: 120.00MB + MeanCPU: 32.645% + ... + ``` + * Whether the kdoctor agent is concurrently executing other tasks and occupying resources can be determined by checking the QPS count of other tasks being executed simultaneously. + You can use the kdoctor aggregate report `kubectl get kdoctorreport` to view the QPS count of other concurrently running tasks. + Stagger the task execution time or isolate the task by defining agentSpec to specify the kdoctor agent to execute the task. Because QPS statistics are time-sensitive, they can be used together with the log as a reference. + Before the task execution starts, the currently executing QPS will be output to the log。 + ```shell + ~kubectl logs kdoctor-agent-74rrp -n kdoctor |grep "Before the current task starts" + {"level":"DEBUG","ts":"2023-11-07T10:01:02.821Z","agent":"agent.agentController.AppHttpHealthyReconciler.AppHttpHealthy.test-task.round1","caller":"pluginManager/agentTools.go:90","msg":"Before the current task starts, the total QPS of the tasks being executed is AppHttpHealth=100,NetReach=0,NetDNS=0","AppHttpHealthy":"test-task"} + ``` + ```shell + ~kubectl get kdoctorreport test-task -oyaml + ... + TotalRunningLoad: + AppHttpHealthyQPS: 100 + NetDnsQPS: 50 + NetReachQPS: 0 + ... + ``` From 96cd5804fad6332ec2be9e464f975a826cca0b52 Mon Sep 17 00:00:00 2001 From: ii2day Date: Thu, 16 Nov 2023 18:19:36 +0800 Subject: [PATCH 17/41] use new dns client to fix data race and reuse connect Signed-off-by: ii2day --- go.mod | 2 + go.sum | 6 +- pkg/loadRequest/loadDns/dns_requester.go | 98 ++-- pkg/loadRequest/loadDns/dns_test.go | 10 +- pkg/loadRequest/loadHttp/http_requester.go | 3 +- pkg/pluginManager/netdns/agentExecuteTask.go | 2 +- pkg/resource/resource.go | 12 +- test/Makefile | 7 +- test/e2e/apphttphealth/apphttphealth_test.go | 40 +- test/e2e/common/constants.go | 1 + test/e2e/common/tools.go | 13 +- test/e2e/netdns/netdns_test.go | 13 +- test/e2e/netreach/netreach_test.go | 13 +- test/e2e/runtime/runtime_test.go | 20 +- vendor/github.com/miekg/dns/LICENSE | 49 +- vendor/github.com/miekg/dns/README.md | 7 + vendor/github.com/miekg/dns/acceptfunc.go | 3 - vendor/github.com/miekg/dns/client.go | 129 +++-- vendor/github.com/miekg/dns/clientconfig.go | 2 +- vendor/github.com/miekg/dns/defaults.go | 54 ++- vendor/github.com/miekg/dns/dnssec.go | 14 +- vendor/github.com/miekg/dns/dnssec_keyscan.go | 3 +- vendor/github.com/miekg/dns/doc.go | 86 ++-- vendor/github.com/miekg/dns/edns.go | 55 +-- vendor/github.com/miekg/dns/fuzz.go | 1 + vendor/github.com/miekg/dns/generate.go | 33 +- vendor/github.com/miekg/dns/labels.go | 2 +- .../miekg/dns/listen_no_reuseport.go | 3 +- .../github.com/miekg/dns/listen_reuseport.go | 2 +- vendor/github.com/miekg/dns/msg.go | 111 +++-- vendor/github.com/miekg/dns/msg_helpers.go | 92 ++-- vendor/github.com/miekg/dns/scan.go | 65 +-- vendor/github.com/miekg/dns/scan_rr.go | 134 +++++- vendor/github.com/miekg/dns/server.go | 4 +- vendor/github.com/miekg/dns/singleinflight.go | 61 --- vendor/github.com/miekg/dns/svcb.go | 102 ++-- vendor/github.com/miekg/dns/tools.go | 1 + vendor/github.com/miekg/dns/types.go | 129 ++++- vendor/github.com/miekg/dns/udp.go | 1 + vendor/github.com/miekg/dns/udp_windows.go | 8 +- vendor/github.com/miekg/dns/version.go | 2 +- vendor/github.com/miekg/dns/xfr.go | 19 +- vendor/github.com/miekg/dns/zduplicate.go | 58 +++ vendor/github.com/miekg/dns/zmsg.go | 110 +++++ vendor/github.com/miekg/dns/ztypes.go | 453 ++++++++++++++++-- vendor/modules.txt | 4 +- 46 files changed, 1419 insertions(+), 618 deletions(-) delete mode 100644 vendor/github.com/miekg/dns/singleinflight.go diff --git a/go.mod b/go.mod index 490eeb37..7a6f08b0 100644 --- a/go.mod +++ b/go.mod @@ -188,3 +188,5 @@ require ( sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) + +replace github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 diff --git a/go.sum b/go.sum index 456bfa6f..a97e34d6 100644 --- a/go.sum +++ b/go.sum @@ -349,6 +349,8 @@ github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 h1:V github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 h1:AlmnjvSvhzwJR9YpV3r0k0b0g6de4infGN0fOImcaO4= +github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= 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.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -380,8 +382,6 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 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.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= 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= @@ -669,7 +669,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -835,7 +834,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index 8f7a8c4e..5143e9b3 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -25,8 +25,8 @@ package loadDns import ( - "context" "crypto/tls" + "fmt" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" "github.com/kdoctor-io/kdoctor/pkg/utils/stats" "github.com/miekg/dns" @@ -144,55 +144,82 @@ func (b *Work) Finish() { b.report.finalize(total) } -func (b *Work) makeRequest(conn *dns.Conn, wg *sync.WaitGroup) { +func (b *Work) makeRequest(client *dns.Client, msg *dns.Msg, conn *dns.Conn, wg *sync.WaitGroup) { defer wg.Done() - var msg *dns.Msg - var rtt time.Duration - var err error + // Due to the time limitation on long-lived TCP connections by CoreDNS, connection reuse is not adopted for the TCP protocol. + if RequestProtocol(b.Protocol) == RequestMethodUdp { + err := client.ExchangeWithReuseConn(msg, conn) + if err != nil { + b.results <- &result{ + duration: 0, + err: err, + msg: nil, + } + } + } else { + r, rtt, err := client.Exchange(msg, b.ServerAddr) + b.results <- &result{ + duration: rtt, + err: err, + msg: r, + } + } + +} + +func (b *Work) runWorker() { + var conn *dns.Conn + var err error client := new(dns.Client) client.Net = b.Protocol client.Timeout = time.Duration(b.Timeout) * time.Millisecond - if b.Protocol == "tcp-tls" { + if RequestProtocol(b.Protocol) == RequestMethodTcpTls { tlsConfig := &tls.Config{ InsecureSkipVerify: true, } client.TLSConfig = tlsConfig } - if b.Protocol == "tcp" || b.Protocol == "tcp-tls" { - msg, rtt, err = client.Exchange(b.Msg, b.ServerAddr) - - } else { - if conn == nil { - conn, _ = client.Dial(b.ServerAddr) + if RequestProtocol(b.Protocol) == RequestMethodUdp { + conn, err = b.makeConn(client) + if err != nil { + b.Logger.Sugar().Errorf("failed create dns conn,err=%v", err) + return } - msg, rtt, err = client.ExchangeWithConn(b.Msg, conn) - } - - b.results <- &result{ - duration: rtt, - err: err, - msg: msg, + go conn.Receiver() + } else { + conn = new(dns.Conn) } -} -func (b *Work) runWorker() { - conn, err := b.makeConn() - if err != nil { - b.Logger.Sugar().Errorf("failed create dns conn,err=%v", err) - return - } wg := &sync.WaitGroup{} for { // Check if application is stopped. Do not send into a closed channel. select { case <-b.stopCh: wg.Wait() + if RequestProtocol(b.Protocol) == RequestMethodUdp { + conn.ShutDownReceiver() + conn.Close() + } return case <-b.qosTokenBucket: wg.Add(1) - go b.makeRequest(conn, wg) + msg := new(dns.Msg) + *msg = *b.Msg + msg.Id = dns.Id() + go b.makeRequest(client, msg, conn, wg) + + case resp := <-conn.ResponseReceiver: + e := resp.Err + if resp.Rtt > time.Duration(b.Timeout)*time.Millisecond { + e = fmt.Errorf("timeout for request, %d more than %d", resp.Rtt.Milliseconds(), b.Timeout) + } + b.results <- &result{ + duration: resp.Rtt, + err: e, + msg: resp.Msg, + } } } } @@ -258,11 +285,20 @@ func (b *Work) AggregateMetric() *v1beta1.DNSMetrics { return metric } -func (b *Work) makeConn() (*dns.Conn, error) { +func (b *Work) makeConn(c *dns.Client) (*dns.Conn, error) { var err error - d := net.Dialer{Timeout: time.Duration(b.Timeout) * time.Millisecond} + d := new(net.Dialer) conn := new(dns.Conn) - conn.Conn, err = d.DialContext(context.Background(), "udp", b.ServerAddr) - + conn.ResponseReceiver = make(chan dns.Response, b.QPS) + conn.ShutDown = make(chan struct{}) + conn.Conn, err = d.Dial(b.Protocol, b.ServerAddr) + // write with the appropriate write timeout + t := time.Now() + writeDeadline := t.Add(time.Duration(b.RequestTimeSecond) * time.Second) + readDeadline := t.Add(time.Duration(b.RequestTimeSecond) * time.Second) + _ = conn.SetWriteDeadline(writeDeadline) + _ = conn.SetReadDeadline(readDeadline) + + conn.TsigSecret, conn.TsigProvider = c.TsigSecret, c.TsigProvider return conn, err } diff --git a/pkg/loadRequest/loadDns/dns_test.go b/pkg/loadRequest/loadDns/dns_test.go index e95b6518..f8f86a46 100644 --- a/pkg/loadRequest/loadDns/dns_test.go +++ b/pkg/loadRequest/loadDns/dns_test.go @@ -25,7 +25,7 @@ var _ = Describe("test dns ", Label("dns"), func() { TargetDomain: "www.baidu.com", DnsServerAddr: dnsServer, PerRequestTimeoutInMs: 5000, - DurationInSecond: 1, + DurationInSecond: 10, Qps: 10, } @@ -55,7 +55,7 @@ var _ = Describe("test dns ", Label("dns"), func() { TargetDomain: "www.baidu.com", DnsServerAddr: dnsServer, PerRequestTimeoutInMs: 5000, - DurationInSecond: 1, + DurationInSecond: 10, Qps: 10, EnableLatencyMetric: true, } @@ -86,7 +86,7 @@ var _ = Describe("test dns ", Label("dns"), func() { TargetDomain: "www.baidu.com", DnsServerAddr: dnsServer, PerRequestTimeoutInMs: 5000, - DurationInSecond: 1, + DurationInSecond: 10, Qps: 10, } @@ -115,7 +115,7 @@ var _ = Describe("test dns ", Label("dns"), func() { TargetDomain: "www.no-existed.com", DnsServerAddr: dnsServer, PerRequestTimeoutInMs: 5000, - DurationInSecond: 1, + DurationInSecond: 10, Qps: 10, } @@ -144,7 +144,7 @@ var _ = Describe("test dns ", Label("dns"), func() { TargetDomain: "wikipedia.org", DnsServerAddr: dnsServer, PerRequestTimeoutInMs: 5000, - DurationInSecond: 1, + DurationInSecond: 10, Qps: 10, } diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index f4b38453..2f5396a8 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -273,6 +273,7 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { } } } + b.results <- &result{ duration: finish, statusCode: statusCode, @@ -310,7 +311,7 @@ func (b *Work) runWorker() { tr.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper) } // Each goroutine uses the same HTTP Client instance - client := &http.Client{Transport: tr, Timeout: time.Duration(b.Timeout) * time.Millisecond} + client := &http.Client{Transport: tr} if b.DisableRedirects { client.CheckRedirect = func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse diff --git a/pkg/pluginManager/netdns/agentExecuteTask.go b/pkg/pluginManager/netdns/agentExecuteTask.go index 3217b0a4..cd4e521c 100644 --- a/pkg/pluginManager/netdns/agentExecuteTask.go +++ b/pkg/pluginManager/netdns/agentExecuteTask.go @@ -218,7 +218,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, } wg.Wait() - logger.Sugar().Infof("plugin finished all http request tests") + logger.Sugar().Infof("plugin finished all dns request tests") // ----------------------- aggregate report task := &v1beta1.NetDNSTask{} diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index dcefd2f5..f5d2c9ee 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -70,11 +70,15 @@ func (r *UsedResource) RunResourceCollector() { func (r *UsedResource) Stats() v1beta1.SystemResource { r.l.Lock() - defer r.l.Unlock() + useCPU := r.cpu + totalCPU := r.totalCPU + roundCount := r.roundCount + mem := r.mem + r.l.Unlock() resource := v1beta1.SystemResource{ - MaxCPU: fmt.Sprintf("%.3f%%", r.cpu), - MeanCPU: fmt.Sprintf("%.3f%%", r.totalCPU/float64(r.roundCount)), - MaxMemory: fmt.Sprintf("%.2fMB", float64(r.mem/(1024*1024))), + MaxCPU: fmt.Sprintf("%.3f%%", useCPU), + MeanCPU: fmt.Sprintf("%.3f%%", totalCPU/float64(roundCount)), + MaxMemory: fmt.Sprintf("%.2fMB", float64(mem/(1024*1024))), } return resource diff --git a/test/Makefile b/test/Makefile index d20b733a..2c6ee0eb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -159,8 +159,10 @@ deploy_project: HELM_OPTION+=" --set kdoctorAgent.ingress.enable=false " ; \ fi ; \ HELM_OPTION+=" --set feature.aggregateReport.enabled=true " ; \ - HELM_OPTION+=" --set kdoctorAgent.resources.requests.cpu=200m " ; \ - HELM_OPTION+=" --set kdoctorAgent.resources.requests.memory=256Mi " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.requests.cpu=400m " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.limits.cpu=1600m " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.requests.memory=512Mi " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.limits.memory=2048Mi " ; \ HELM_OPTION+=" --set feature.aggregateReport.controller.reportHostPath=/var/run/kdoctor/controller " ; \ HELM_OPTION+=" --set kdoctorAgent.debug.logLevel=debug --set kdoctorController.debug.logLevel=debug " ; \ HELM_OPTION+=" --set kdoctorAgent.prometheus.enabled=true --set kdoctorController.prometheus.enabled=true " ; \ @@ -172,7 +174,6 @@ deploy_project: || { KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) ./scripts/debugCluster.sh $(KIND_KUBECONFIG) "detail" $(E2E_INSTALL_NAMESPACE) ; exit 1 ; } ; \ exit 0 - #========================= .PHONY: deploy_multus diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 93fdbd20..d499f695 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -25,7 +25,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("success http testing appHttpHealth method GET", Label("A00001", "A00011", "C00006", "E00002", "A00014"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -51,6 +51,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -87,7 +88,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("failed http testing appHttpHealth due to status code", Label("A00002"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" expectStatusCode := 205 appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -115,6 +116,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -177,6 +179,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -213,7 +216,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("success https testing appHttpHealth method GET", Label("A00004"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) @@ -240,6 +243,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } target.TlsSecretName = &common.TlsClientName target.TlsSecretNamespace = &common.TestNameSpace + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -276,7 +280,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("failed https testing appHttpHealth due to tls", Label("A00005"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -305,6 +309,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } target.TlsSecretName = &common.TlsClientName target.TlsSecretNamespace = &common.TestNameSpace + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -341,7 +346,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully http testing appHttpHealth method PUT ", Label("A00006"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-put" + tools.RandomName() @@ -367,6 +372,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -404,7 +410,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully http testing appHttpHealth method POST With Body", Label("A00007"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-post" + tools.RandomName() @@ -433,6 +439,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { target.BodyConfigName = &bodyConfigMapName target.BodyConfigNamespace = &common.TestNameSpace target.Header = []string{"Content-Type: application/json"} + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -469,7 +476,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully http testing appHttpHealth method HEAD", Label("A00008"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-head" + tools.RandomName() @@ -495,6 +502,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -531,7 +539,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully http testing appHttpHealth method PATCH", Label("A00009"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-patch" + tools.RandomName() @@ -557,6 +565,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -593,7 +602,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully http testing appHttpHealth method OPTIONS", Label("A00010"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-options" + tools.RandomName() @@ -619,6 +628,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -681,6 +691,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -717,7 +728,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully https testing appHttpHealth method GET Protocol Http2", Label("A00013"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -746,6 +757,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { target.TlsSecretName = &common.TlsClientName target.TlsSecretNamespace = &common.TestNameSpace target.Http2 = true + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -782,7 +794,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { It("Successfully testing using default daemonSet as workload with Task AppHttpHealthy ", Label("E00014"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -803,6 +815,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -834,10 +847,10 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { }) - It("Successfully testing using default daemonSet as workload with Task AppHttpHealthy ", Label("E00017"), func() { + It("Successfully testing using default daemonSet as workload with more Task AppHttpHealthy", Label("E00017"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -858,6 +871,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { } else { target.Host = fmt.Sprintf("http://%s:%d?task=%s", testSvcIP, httpPort, appHttpHealthName) } + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request diff --git a/test/e2e/common/constants.go b/test/e2e/common/constants.go index 3244d9ab..daa59d4f 100644 --- a/test/e2e/common/constants.go +++ b/test/e2e/common/constants.go @@ -11,6 +11,7 @@ const ( PluginReportPath = "/apis/system.kdoctor.io/v1beta1/namespaces/default/kdoctorreports/" KDoctorCaName = "kdoctor-ca" KdoctorTestTokenSecretName = "apiserver-token" + RequestFaultRate = 0.1 ) var ( diff --git a/test/e2e/common/tools.go b/test/e2e/common/tools.go index a31e037f..c476e2e6 100644 --- a/test/e2e/common/tools.go +++ b/test/e2e/common/tools.go @@ -112,7 +112,6 @@ func WaitKdoctorTaskDone(f *frame.Framework, task client.Object, taskKind string default: return fmt.Errorf("unknown task type: %s", task.GetObjectKind().GroupVersionKind().Kind) } - } func GetKdoctorToken(f *frame.Framework) (string, error) { @@ -249,8 +248,8 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n // qps expectRequestCount := float64(rs.Spec.Request.QPS * rs.Spec.Request.DurationInSecond) realRequestCount := float64(m.Metrics.RequestCounts) - if math.Abs(realRequestCount-expectRequestCount)/expectRequestCount > 0.05 { - return GetResultFromReport(r), fmt.Errorf("The error in the number of requests is greater than 0.05 ,real request count: %d,expect request count:%d", int(realRequestCount), int(expectRequestCount)) + if math.Abs(realRequestCount-expectRequestCount)/expectRequestCount > RequestFaultRate { + return GetResultFromReport(r), fmt.Errorf("the error in the number of requests is greater than %.2f ,real request count: %d,expect request count:%d", RequestFaultRate, int(realRequestCount), int(expectRequestCount)) } if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") @@ -301,8 +300,8 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n realCount := float64(m.Metrics.RequestCounts) // report request count reportRequestCount += m.Metrics.RequestCounts - if math.Abs(realCount-expectCount)/expectCount > 0.05 { - return GetResultFromReport(r), fmt.Errorf("The error in the number of requests is greater than 0.05 ,real request count: %d,expect request count:%d", int(realCount), int(expectCount)) + if math.Abs(realCount-expectCount)/expectCount > RequestFaultRate { + return GetResultFromReport(r), fmt.Errorf("The error in the number of requests is greater than %.2f ,real request count: %d,expect request count:%d", RequestFaultRate, int(realCount), int(expectCount)) } if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") @@ -366,8 +365,8 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n realCount := float64(m.Metrics.RequestCounts) // report request count reportRequestCount += m.Metrics.RequestCounts - if math.Abs(realCount-expectCount)/expectCount > 0.05 { - return GetResultFromReport(r), fmt.Errorf("The error in the number of requests is greater than 0.05, real request count: %d,expect request count:%d ", int(realCount), int(expectCount)) + if math.Abs(realCount-expectCount)/expectCount > RequestFaultRate { + return GetResultFromReport(r), fmt.Errorf("The error in the number of requests is greater than %.2f, real request count: %d,expect request count:%d ", RequestFaultRate, int(realCount), int(expectCount)) } if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") diff --git a/test/e2e/netdns/netdns_test.go b/test/e2e/netdns/netdns_test.go index 479fc913..07376e48 100644 --- a/test/e2e/netdns/netdns_test.go +++ b/test/e2e/netdns/netdns_test.go @@ -47,6 +47,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { targetDns.ServiceName = &KubeDnsName targetDns.ServiceNamespace = &KubeDnsNamespace target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true netDns.Spec.Target = target // request @@ -112,6 +113,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { port := 53 targetDnsUser.Port = &port target.NetDnsTargetUser = targetDnsUser + target.EnableLatencyMetric = true netDns.Spec.Target = target // request @@ -172,14 +174,15 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { targetDns.ServiceName = &KubeDnsName targetDns.ServiceNamespace = &KubeDnsNamespace target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 1500 request.QPS = 5 request.DurationInSecond = 5 - request.Domain = fmt.Sprintf(targetDomain, netDnsName) + request.Domain = "kubernetes.default.svc.cluster.local" protocol := "udp" request.Protocol = &protocol netDns.Spec.Request = request @@ -230,14 +233,15 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { targetDns.ServiceName = &KubeDnsName targetDns.ServiceNamespace = &KubeDnsNamespace target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 1500 request.QPS = 5 request.DurationInSecond = 5 - request.Domain = fmt.Sprintf(targetDomain, netDnsName) + request.Domain = "kubernetes.default.svc.cluster.local" protocol := "udp" request.Protocol = &protocol netDns.Spec.Request = request @@ -292,6 +296,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { port := 53 targetDnsUser.Port = &port target.NetDnsTargetUser = targetDnsUser + target.EnableLatencyMetric = true netDns.Spec.Target = target // request diff --git a/test/e2e/netreach/netreach_test.go b/test/e2e/netreach/netreach_test.go index 447e1d05..6671e13c 100644 --- a/test/e2e/netreach/netreach_test.go +++ b/test/e2e/netreach/netreach_test.go @@ -14,12 +14,12 @@ import ( var _ = Describe("testing netReach ", Label("netReach"), func() { var termMin = int64(1) - // 1000ms is not stable on GitHub ci, so increased to 3000ms - var requestTimeout = 3000 + // 1000ms is not stable on GitHub ci, so increased to 7000ms + var requestTimeout = 7000 It("success testing netReach", Label("B00001", "C00004", "E00001"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(4000) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -52,6 +52,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { target.MultusInterface = &disable target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 + target.EnableLatencyMetric = true netReach.Spec.Target = target // request @@ -88,7 +89,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { It("Successfully testing using default daemonSet as workload with Task NetReach", Label("E00013"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(4000) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -117,6 +118,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 netReach.Spec.Target = target + target.EnableLatencyMetric = true // request request := new(v1beta1.NetHttpRequest) @@ -150,7 +152,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { It("Successfully testing using default daemonSet as workload with more Task NetReach", Label("E00016"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(4000) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -178,6 +180,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { target.MultusInterface = &disable target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 + target.EnableLatencyMetric = true netReach.Spec.Target = target // request diff --git a/test/e2e/runtime/runtime_test.go b/test/e2e/runtime/runtime_test.go index 604cec8f..bda9b75d 100644 --- a/test/e2e/runtime/runtime_test.go +++ b/test/e2e/runtime/runtime_test.go @@ -23,7 +23,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { It("Successfully testing cascading deletion with Task NetReach DaemonSet Service and Ingress ", Label("E00007"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -57,6 +57,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { target.MultusInterface = &disable target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 + target.EnableLatencyMetric = true netReach.Spec.Target = target // request @@ -100,7 +101,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { It("Successfully testing cascading deletion with Task NetAppHttpHealthy DaemonSet Service", Label("E00008"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -122,6 +123,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { target := new(v1beta1.AppHttpHealthyTarget) target.Method = "GET" target.Host = "www.baidu.com" + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -193,11 +195,12 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { targetDns.ServiceName = &ServiceName targetDns.ServiceNamespace = &ServiceNamespace target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 3000 request.QPS = 10 request.DurationInSecond = 10 request.Domain = "www.baidu.com" @@ -240,7 +243,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { It("Successfully testing cascading deletion with Task NetReach Deployment Service and Ingress ", Label("E00010"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -276,6 +279,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { target.MultusInterface = &disable target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 + target.EnableLatencyMetric = true netReach.Spec.Target = target // request @@ -319,7 +323,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { It("Successfully testing cascading deletion with Task NetAppHttpHealthy Deployment Service", Label("E00011"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -343,6 +347,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { target := new(v1beta1.AppHttpHealthyTarget) target.Method = "GET" target.Host = "www.baidu.com" + target.EnableLatencyMetric = true appHttpHealth.Spec.Target = target // request @@ -386,7 +391,7 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { It("Successfully testing cascading deletion with Task NetDns Deployment Service ", Label("E00012"), func() { var e error successRate := float64(1) - successMean := int64(1500) + successMean := int64(3000) crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() @@ -416,11 +421,12 @@ var _ = Describe("testing runtime ", Label("runtime"), func() { targetDns.ServiceName = &ServiceName targetDns.ServiceNamespace = &ServiceNamespace target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 3000 request.QPS = 10 request.DurationInSecond = 10 request.Domain = "www.baidu.com" diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE index 55f12ab7..852ab9ce 100644 --- a/vendor/github.com/miekg/dns/LICENSE +++ b/vendor/github.com/miekg/dns/LICENSE @@ -1,30 +1,29 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +BSD 3-Clause License + +Copyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. +All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -As this is fork of the official Go code the same license applies. -Extensions of the original work are copyright (c) 2011 Miek Gieben +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 5a799d88..95bc08d5 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -77,6 +77,11 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://ping.sx/dig * https://fleetdeck.io/ * https://github.com/markdingo/autoreverse +* https://github.com/slackhq/nebula +* https://addr.tools/ +* https://dnscheck.tools/ +* https://github.com/egbakou/domainverifier +* https://github.com/semihalev/sdns Send pull request if you want to be listed here. @@ -140,6 +145,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY * 3597 - Unknown RRs +* 4025 - A Method for Storing IPsec Keying Material in DNS * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP record * 4343 - Case insensitivity @@ -175,6 +181,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 8080 - EdDSA for DNSSEC * 8499 - DNS Terminology * 8659 - DNS Certification Authority Authorization (CAA) Resource Record +* 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery * 8914 - Extended DNS Errors * 8976 - Message Digest for DNS Zones (ZONEMD RR) diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go index ac479db9..1a59a854 100644 --- a/vendor/github.com/miekg/dns/acceptfunc.go +++ b/vendor/github.com/miekg/dns/acceptfunc.go @@ -10,8 +10,6 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction // // * opcode isn't OpcodeQuery or OpcodeNotify // -// * Zero bit isn't zero -// // * does not have exactly 1 question in the question section // // * has more than 1 RR in the Answer section @@ -19,7 +17,6 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction // * has more than 0 RRs in the Authority section // // * has more than 2 RRs in the Additional section -// var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc // MsgAcceptAction represents the action to be taken. diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 9aa65853..b5f40e16 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -10,6 +10,7 @@ import ( "io" "net" "strings" + "sync" "time" ) @@ -30,13 +31,22 @@ func isPacketConn(c net.Conn) bool { return true } +type Response struct { + Msg *Msg + Rtt time.Duration + Err error +} + // A Conn represents a connection to a DNS server. type Conn struct { - net.Conn // a net.Conn holding the connection - UDPSize uint16 // minimum receive buffer for UDP messages - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. - tsigRequestMAC string + net.Conn // a net.Conn holding the connection + UDPSize uint16 // minimum receive buffer for UDP messages + TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) + TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. + tsigRequestMAC string + ResponseReceiver chan Response + SendStartTime sync.Map + ShutDown chan struct{} } func (co *Conn) tsigProvider() TsigProvider { @@ -47,6 +57,38 @@ func (co *Conn) tsigProvider() TsigProvider { return tsigSecretProvider(co.TsigSecret) } +func (co *Conn) ShutDownReceiver() { + co.ShutDown <- struct{}{} + co.Close() + close(co.ResponseReceiver) +} + +// Receiver Connection multiplexing result receiver, +// collects results from the connection and differentiates requests based on msg.id +func (co *Conn) Receiver() { + for { + select { + case <-co.ShutDown: + break + default: + r, err := co.ReadMsg() + if r == nil { + continue + } + conResponse := Response{} + conResponse.Msg = r + conResponse.Err = err + startTime, ok := co.SendStartTime.LoadAndDelete(r.Id) + if !ok { + err = fmt.Errorf("no record msg id") + } else { + conResponse.Rtt = time.Since(startTime.(time.Time)) + } + co.ResponseReceiver <- conResponse + } + } +} + // A Client defines parameters for a DNS client. type Client struct { Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) @@ -56,14 +98,20 @@ type Client struct { // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout, // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and // Client.Dialer) or context.Context.Deadline (see ExchangeContext) - Timeout time.Duration - DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. - SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass - group singleflight + Timeout time.Duration + DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) + TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. + + // SingleInflight previously serialised multiple concurrent queries for the + // same Qname, Qtype and Qclass to ensure only one would be in flight at a + // time. + // + // Deprecated: This is a no-op. Callers should implement their own in flight + // query caching if needed. See github.com/miekg/dns/issues/1449. + SingleInflight bool } // Exchange performs a synchronous UDP query. It sends the message m to the address @@ -106,7 +154,6 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { } // DialContext connects to the address on the named network, with a context.Context. -// For TLS over TCP (DoT) the context isn't used yet. This will be enabled when Go 1.18 is released. func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, err error) { // create a new dialer with the appropriate timeout var d net.Dialer @@ -127,15 +174,11 @@ func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, e if useTLS { network = strings.TrimSuffix(network, "-tls") - // TODO(miekg): Enable after Go 1.18 is released, to be able to support two prev. releases. - /* - tlsDialer := tls.Dialer{ - NetDialer: &d, - Config: c.TLSConfig, - } - conn.Conn, err = tlsDialer.DialContext(ctx, network, address) - */ - conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig) + tlsDialer := tls.Dialer{ + NetDialer: &d, + Config: c.TLSConfig, + } + conn.Conn, err = tlsDialer.DialContext(ctx, network, address) } else { conn.Conn, err = d.DialContext(ctx, network, address) } @@ -183,33 +226,13 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er // This allows users of the library to implement their own connection management, // as opposed to Exchange, which will always use new connections and incur the added overhead // that entails when using "tcp" and especially "tcp-tls" clients. -// -// When the singleflight is set for this client the context is _not_ forwarded to the (shared) exchange, to -// prevent one cancelation from canceling all outstanding requests. func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { - return c.exchangeWithConnContext(context.Background(), m, conn) + return c.ExchangeWithConnContext(context.Background(), m, conn) } -func (c *Client) exchangeWithConnContext(ctx context.Context, m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { - if !c.SingleInflight { - return c.exchangeContext(ctx, m, conn) - } - - q := m.Question[0] - key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass) - r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) { - // When we're doing singleflight we don't want one context cancelation, cancel _all_ outstanding queries. - // Hence we ignore the context and use Background(). - return c.exchangeContext(context.Background(), m, conn) - }) - if r != nil && shared { - r = r.Copy() - } - - return r, rtt, err -} - -func (c *Client) exchangeContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { +// ExchangeWithConnContext has the same behaviour as ExchangeWithConn and +// additionally obeys deadlines from the passed Context. +func (c *Client) ExchangeWithConnContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { opt := m.IsEdns0() // If EDNS0 is used use that for size. if opt != nil && opt.UDPSize() >= MinMsgSize { @@ -260,6 +283,15 @@ func (c *Client) exchangeContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, return r, rtt, err } +// ExchangeWithReuseConn Supports one link to send multiple requests msg, +// use co.Receive() to open the receiver to receive connection returns, +// and receive return results from ResponseReceiver +func (c *Client) ExchangeWithReuseConn(m *Msg, co *Conn) (err error) { + co.SendStartTime.Store(m.Id, time.Now()) + err = co.WriteMsg(m) + return err +} + // ReadMsg reads a message from the connection co. // If the received message contains a TSIG record the transaction signature // is verified. This method always tries to return the message, however if an @@ -431,7 +463,6 @@ func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) // co.WriteMsg(m) // in, _ := co.ReadMsg() // co.Close() -// func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { println("dns: ExchangeConn: this function is deprecated") co := new(Conn) @@ -480,5 +511,5 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, } defer conn.Close() - return c.exchangeWithConnContext(ctx, m, conn) + return c.ExchangeWithConnContext(ctx, m, conn) } diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go index e11b630d..d00ac62f 100644 --- a/vendor/github.com/miekg/dns/clientconfig.go +++ b/vendor/github.com/miekg/dns/clientconfig.go @@ -68,7 +68,7 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) { } case "search": // set search path to given servers - c.Search = append([]string(nil), f[1:]...) + c.Search = cloneSlice(f[1:]) case "options": // magic options for _, s := range f[1:] { diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index f2cdbf43..02d9199a 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -22,8 +22,7 @@ func (dns *Msg) SetReply(request *Msg) *Msg { } dns.Rcode = RcodeSuccess if len(request.Question) > 0 { - dns.Question = make([]Question, 1) - dns.Question[0] = request.Question[0] + dns.Question = []Question{request.Question[0]} } return dns } @@ -208,7 +207,7 @@ func IsDomainName(s string) (labels int, ok bool) { } // check for \DDD - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + if isDDD(s[i+1:]) { i += 3 begin += 3 } else { @@ -272,40 +271,39 @@ func IsMsg(buf []byte) error { // IsFqdn checks if a domain name is fully qualified. func IsFqdn(s string) bool { - s2 := strings.TrimSuffix(s, ".") - if s == s2 { + // Check for (and remove) a trailing dot, returning if there isn't one. + if s == "" || s[len(s)-1] != '.' { return false } + s = s[:len(s)-1] - i := strings.LastIndexFunc(s2, func(r rune) bool { + // If we don't have an escape sequence before the final dot, we know it's + // fully qualified and can return here. + if s == "" || s[len(s)-1] != '\\' { + return true + } + + // Otherwise we have to check if the dot is escaped or not by checking if + // there are an odd or even number of escape sequences before the dot. + i := strings.LastIndexFunc(s, func(r rune) bool { return r != '\\' }) - - // Test whether we have an even number of escape sequences before - // the dot or none. - return (len(s2)-i)%2 != 0 + return (len(s)-i)%2 != 0 } -// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181. -// This means the RRs need to have the same type, name, and class. Returns true -// if the RR set is valid, otherwise false. +// IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181. +// This means the RRs need to have the same type, name, and class. func IsRRset(rrset []RR) bool { if len(rrset) == 0 { return false } - if len(rrset) == 1 { - return true - } - rrHeader := rrset[0].Header() - rrType := rrHeader.Rrtype - rrClass := rrHeader.Class - rrName := rrHeader.Name + baseH := rrset[0].Header() for _, rr := range rrset[1:] { - curRRHeader := rr.Header() - if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName { + curH := rr.Header() + if curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name { // Mismatch between the records, so this is not a valid rrset for - //signing/verifying + // signing/verifying return false } } @@ -323,9 +321,15 @@ func Fqdn(s string) string { } // CanonicalName returns the domain name in canonical form. A name in canonical -// form is lowercase and fully qualified. See Section 6.2 in RFC 4034. +// form is lowercase and fully qualified. Only US-ASCII letters are affected. See +// Section 6.2 in RFC 4034. func CanonicalName(s string) string { - return strings.ToLower(Fqdn(s)) + return strings.Map(func(r rune) rune { + if r >= 'A' && r <= 'Z' { + r += 'a' - 'A' + } + return r + }, Fqdn(s)) } // Copied from the official Go code. diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index ea01aa81..1be87eae 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -128,10 +128,6 @@ type dnskeyWireFmt struct { /* Nothing is left out */ } -func divRoundUp(a, b int) int { - return (a + b - 1) / b -} - // KeyTag calculates the keytag (or key-id) of the DNSKEY. func (k *DNSKEY) KeyTag() uint16 { if k == nil { @@ -417,11 +413,11 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { return err } - sigbuf := rr.sigBuf() // Get the binary signature data - if rr.Algorithm == PRIVATEDNS { // PRIVATEOID - // TODO(miek) - // remove the domain name and assume its ours? - } + sigbuf := rr.sigBuf() // Get the binary signature data + // TODO(miek) + // remove the domain name and assume its ours? + // if rr.Algorithm == PRIVATEDNS { // PRIVATEOID + // } h, cryptohash, err := hashFromAlgorithm(rr.Algorithm) if err != nil { diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go index f7965816..5e72249b 100644 --- a/vendor/github.com/miekg/dns/dnssec_keyscan.go +++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go @@ -37,7 +37,8 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er return nil, ErrPrivKey } // TODO(mg): check if the pubkey matches the private key - algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8) + algoStr, _, _ := strings.Cut(m["algorithm"], " ") + algo, err := strconv.ParseUint(algoStr, 10, 8) if err != nil { return nil, ErrPrivKey } diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index f00f5722..586ab691 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -13,28 +13,28 @@ names in a message will result in a packing failure. Resource records are native types. They are not stored in wire format. Basic usage pattern for creating a new resource record: - r := new(dns.MX) - r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} - r.Preference = 10 - r.Mx = "mx.miek.nl." + r := new(dns.MX) + r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} + r.Preference = 10 + r.Mx = "mx.miek.nl." Or directly from a string: - mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") + mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") Or when the default origin (.) and TTL (3600) and class (IN) suit you: - mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") + mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") Or even: - mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") + mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") In the DNS messages are exchanged, these messages contain resource records (sets). Use pattern for creating a message: - m := new(dns.Msg) - m.SetQuestion("miek.nl.", dns.TypeMX) + m := new(dns.Msg) + m.SetQuestion("miek.nl.", dns.TypeMX) Or when not certain if the domain name is fully qualified: @@ -45,17 +45,17 @@ records for the miek.nl. zone. The following is slightly more verbose, but more flexible: - m1 := new(dns.Msg) - m1.Id = dns.Id() - m1.RecursionDesired = true - m1.Question = make([]dns.Question, 1) - m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.RecursionDesired = true + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} After creating a message it can be sent. Basic use pattern for synchronous querying the DNS at a server configured on 127.0.0.1 and port 53: - c := new(dns.Client) - in, rtt, err := c.Exchange(m1, "127.0.0.1:53") + c := new(dns.Client) + in, rtt, err := c.Exchange(m1, "127.0.0.1:53") Suppressing multiple outstanding queries (with the same question, type and class) is as easy as setting: @@ -72,7 +72,7 @@ and port to use for the connection: Port: 12345, Zone: "", } - c.Dialer := &net.Dialer{ + c.Dialer = &net.Dialer{ Timeout: 200 * time.Millisecond, LocalAddr: &laddr, } @@ -96,7 +96,7 @@ the Answer section: // do something with t.Txt } -Domain Name and TXT Character String Representations +# Domain Name and TXT Character String Representations Both domain names and TXT character strings are converted to presentation form both when unpacked and when converted to strings. @@ -108,7 +108,7 @@ be escaped. Bytes below 32 and above 127 will be converted to \DDD form. For domain names, in addition to the above rules brackets, periods, spaces, semicolons and the at symbol are escaped. -DNSSEC +# DNSSEC DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses public key cryptography to sign resource records. The public keys are stored in @@ -117,12 +117,12 @@ DNSKEY records and the signatures in RRSIG records. Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit to a request. - m := new(dns.Msg) - m.SetEdns0(4096, true) + m := new(dns.Msg) + m.SetEdns0(4096, true) Signature generation, signature verification and key generation are all supported. -DYNAMIC UPDATES +# DYNAMIC UPDATES Dynamic updates reuses the DNS message format, but renames three of the sections. Question is Zone, Answer is Prerequisite, Authority is Update, only @@ -133,30 +133,30 @@ certain resource records or names in a zone to specify if resource records should be added or removed. The table from RFC 2136 supplemented with the Go DNS function shows which functions exist to specify the prerequisites. - 3.2.4 - Table Of Metavalues Used In Prerequisite Section + 3.2.4 - Table Of Metavalues Used In Prerequisite Section - CLASS TYPE RDATA Meaning Function - -------------------------------------------------------------- - ANY ANY empty Name is in use dns.NameUsed - ANY rrset empty RRset exists (value indep) dns.RRsetUsed - NONE ANY empty Name is not in use dns.NameNotUsed - NONE rrset empty RRset does not exist dns.RRsetNotUsed - zone rrset rr RRset exists (value dep) dns.Used + CLASS TYPE RDATA Meaning Function + -------------------------------------------------------------- + ANY ANY empty Name is in use dns.NameUsed + ANY rrset empty RRset exists (value indep) dns.RRsetUsed + NONE ANY empty Name is not in use dns.NameNotUsed + NONE rrset empty RRset does not exist dns.RRsetNotUsed + zone rrset rr RRset exists (value dep) dns.Used The prerequisite section can also be left empty. If you have decided on the prerequisites you can tell what RRs should be added or deleted. The next table shows the options you have and what functions to call. - 3.4.2.6 - Table Of Metavalues Used In Update Section + 3.4.2.6 - Table Of Metavalues Used In Update Section - CLASS TYPE RDATA Meaning Function - --------------------------------------------------------------- - ANY ANY empty Delete all RRsets from name dns.RemoveName - ANY rrset empty Delete an RRset dns.RemoveRRset - NONE rrset rr Delete an RR from RRset dns.Remove - zone rrset rr Add to an RRset dns.Insert + CLASS TYPE RDATA Meaning Function + --------------------------------------------------------------- + ANY ANY empty Delete all RRsets from name dns.RemoveName + ANY rrset empty Delete an RRset dns.RemoveRRset + NONE rrset rr Delete an RR from RRset dns.Remove + zone rrset rr Add to an RRset dns.Insert -TRANSACTION SIGNATURE +# TRANSACTION SIGNATURE An TSIG or transaction signature adds a HMAC TSIG record to each message sent. The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512. @@ -239,7 +239,7 @@ Basic use pattern validating and replying to a message that has TSIG set. w.WriteMsg(m) } -PRIVATE RRS +# PRIVATE RRS RFC 6895 sets aside a range of type codes for private use. This range is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these @@ -248,7 +248,7 @@ can be used, before requesting an official type code from IANA. See https://miek.nl/2014/september/21/idn-and-private-rr-in-go-dns/ for more information. -EDNS0 +# EDNS0 EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by RFC 6891. It defines a new RR type, the OPT RR, which is then completely @@ -279,9 +279,9 @@ SIG(0) From RFC 2931: - SIG(0) provides protection for DNS transactions and requests .... - ... protection for glue records, DNS requests, protection for message headers - on requests and responses, and protection of the overall integrity of a response. + SIG(0) provides protection for DNS transactions and requests .... + ... protection for glue records, DNS requests, protection for message headers + on requests and responses, and protection of the overall integrity of a response. It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared secret approach in TSIG. Supported algorithms: ECDSAP256SHA256, diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 14568c2e..1b58e8f0 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -78,7 +78,10 @@ func (rr *OPT) String() string { if rr.Do() { s += "flags: do; " } else { - s += "flags: ; " + s += "flags:; " + } + if rr.Hdr.Ttl&0x7FFF != 0 { + s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF) } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) @@ -98,6 +101,8 @@ func (rr *OPT) String() string { s += "\n; SUBNET: " + o.String() case *EDNS0_COOKIE: s += "\n; COOKIE: " + o.String() + case *EDNS0_EXPIRE: + s += "\n; EXPIRE: " + o.String() case *EDNS0_TCP_KEEPALIVE: s += "\n; KEEPALIVE: " + o.String() case *EDNS0_UL: @@ -180,7 +185,7 @@ func (rr *OPT) Do() bool { // SetDo sets the DO (DNSSEC OK) bit. // If we pass an argument, set the DO bit to that value. -// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored. +// It is possible to pass 2 or more arguments, but they will be ignored. func (rr *OPT) SetDo(do ...bool) { if len(do) == 1 { if do[0] { @@ -258,7 +263,7 @@ func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_SUBNET) -// e.Code = dns.EDNS0SUBNET +// e.Code = dns.EDNS0SUBNET // by default this is filled in through unpacking OPT packets (unpackDataOpt) // e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 // e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6 // e.SourceScope = 0 @@ -503,6 +508,7 @@ func (e *EDNS0_LLQ) String() string { " " + strconv.FormatUint(uint64(e.LeaseLife), 10) return s } + func (e *EDNS0_LLQ) copy() EDNS0 { return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} } @@ -515,8 +521,8 @@ type EDNS0_DAU struct { // Option implements the EDNS0 interface. func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } -func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_DAU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DAU) String() string { s := "" @@ -539,8 +545,8 @@ type EDNS0_DHU struct { // Option implements the EDNS0 interface. func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } -func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_DHU) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_DHU) String() string { s := "" @@ -563,8 +569,8 @@ type EDNS0_N3U struct { // Option implements the EDNS0 interface. func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } -func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil } +func (e *EDNS0_N3U) pack() ([]byte, error) { return cloneSlice(e.AlgCode), nil } +func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = cloneSlice(b); return nil } func (e *EDNS0_N3U) String() string { // Re-use the hash map @@ -641,30 +647,21 @@ type EDNS0_LOCAL struct { // Option implements the EDNS0 interface. func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } + func (e *EDNS0_LOCAL) String() string { return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) } + func (e *EDNS0_LOCAL) copy() EDNS0 { - b := make([]byte, len(e.Data)) - copy(b, e.Data) - return &EDNS0_LOCAL{e.Code, b} + return &EDNS0_LOCAL{e.Code, cloneSlice(e.Data)} } func (e *EDNS0_LOCAL) pack() ([]byte, error) { - b := make([]byte, len(e.Data)) - copied := copy(b, e.Data) - if copied != len(e.Data) { - return nil, ErrBuf - } - return b, nil + return cloneSlice(e.Data), nil } func (e *EDNS0_LOCAL) unpack(b []byte) error { - e.Data = make([]byte, len(b)) - copied := copy(e.Data, b) - if copied != len(b) { - return ErrBuf - } + e.Data = cloneSlice(b) return nil } @@ -727,14 +724,10 @@ type EDNS0_PADDING struct { // Option implements the EDNS0 interface. func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } -func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } -func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil } +func (e *EDNS0_PADDING) pack() ([]byte, error) { return cloneSlice(e.Padding), nil } +func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = cloneSlice(b); return nil } func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } -func (e *EDNS0_PADDING) copy() EDNS0 { - b := make([]byte, len(e.Padding)) - copy(b, e.Padding) - return &EDNS0_PADDING{b} -} +func (e *EDNS0_PADDING) copy() EDNS0 { return &EDNS0_PADDING{cloneSlice(e.Padding)} } // Extended DNS Error Codes (RFC 8914). const ( @@ -821,7 +814,7 @@ func (e *EDNS0_EDE) String() string { func (e *EDNS0_EDE) pack() ([]byte, error) { b := make([]byte, 2+len(e.ExtraText)) binary.BigEndian.PutUint16(b[0:], e.InfoCode) - copy(b[2:], []byte(e.ExtraText)) + copy(b[2:], e.ExtraText) return b, nil } diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go index 57410acd..505ae430 100644 --- a/vendor/github.com/miekg/dns/fuzz.go +++ b/vendor/github.com/miekg/dns/fuzz.go @@ -1,3 +1,4 @@ +//go:build fuzz // +build fuzz package dns diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go index ac8df34d..713e9d2d 100644 --- a/vendor/github.com/miekg/dns/generate.go +++ b/vendor/github.com/miekg/dns/generate.go @@ -35,17 +35,17 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { token = token[:i] } - sx := strings.SplitN(token, "-", 2) - if len(sx) != 2 { + startStr, endStr, ok := strings.Cut(token, "-") + if !ok { return zp.setParseError("bad start-stop in $GENERATE range", l) } - start, err := strconv.ParseInt(sx[0], 10, 64) + start, err := strconv.ParseInt(startStr, 10, 64) if err != nil { return zp.setParseError("bad start in $GENERATE range", l) } - end, err := strconv.ParseInt(sx[1], 10, 64) + end, err := strconv.ParseInt(endStr, 10, 64) if err != nil { return zp.setParseError("bad stop in $GENERATE range", l) } @@ -54,7 +54,7 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { } // _BLANK - l, ok := zp.c.Next() + l, ok = zp.c.Next() if !ok || l.value != zBlank { return zp.setParseError("garbage after $GENERATE range", l) } @@ -211,15 +211,16 @@ func (r *generateReader) ReadByte() (byte, error) { func modToPrintf(s string) (string, int64, string) { // Modifier is { offset [ ,width [ ,base ] ] } - provide default // values for optional width and type, if necessary. - var offStr, widthStr, base string - switch xs := strings.Split(s, ","); len(xs) { - case 1: - offStr, widthStr, base = xs[0], "0", "d" - case 2: - offStr, widthStr, base = xs[0], xs[1], "d" - case 3: - offStr, widthStr, base = xs[0], xs[1], xs[2] - default: + offStr, s, ok0 := strings.Cut(s, ",") + widthStr, s, ok1 := strings.Cut(s, ",") + base, _, ok2 := strings.Cut(s, ",") + if !ok0 { + widthStr = "0" + } + if !ok1 { + base = "d" + } + if ok2 { return "", 0, "bad modifier in $GENERATE" } @@ -234,8 +235,8 @@ func modToPrintf(s string) (string, int64, string) { return "", 0, "bad offset in $GENERATE" } - width, err := strconv.ParseInt(widthStr, 10, 64) - if err != nil || width < 0 || width > 255 { + width, err := strconv.ParseUint(widthStr, 10, 8) + if err != nil { return "", 0, "bad width in $GENERATE" } diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index f9faacfe..cd498d2e 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -122,7 +122,7 @@ func Split(s string) []int { } // NextLabel returns the index of the start of the next label in the -// string s starting at offset. +// string s starting at offset. A negative offset will cause a panic. // The bool end is true when the end of the string has been reached. // Also see PrevLabel. func NextLabel(s string, offset int) (i int, end bool) { diff --git a/vendor/github.com/miekg/dns/listen_no_reuseport.go b/vendor/github.com/miekg/dns/listen_no_reuseport.go index b9201417..6ed50f86 100644 --- a/vendor/github.com/miekg/dns/listen_no_reuseport.go +++ b/vendor/github.com/miekg/dns/listen_no_reuseport.go @@ -1,4 +1,5 @@ -// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd package dns diff --git a/vendor/github.com/miekg/dns/listen_reuseport.go b/vendor/github.com/miekg/dns/listen_reuseport.go index fad195cf..89bac903 100644 --- a/vendor/github.com/miekg/dns/listen_reuseport.go +++ b/vendor/github.com/miekg/dns/listen_reuseport.go @@ -1,4 +1,4 @@ -// +build go1.11 +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd // +build aix darwin dragonfly freebsd linux netbsd openbsd package dns diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 89ebb64a..8294d039 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -252,7 +252,7 @@ loop: } // check for \DDD - if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { + if isDDD(bs[i+1:]) { bs[i] = dddToByte(bs[i+1:]) copy(bs[i+1:ls-3], bs[i+4:]) ls -= 3 @@ -448,7 +448,7 @@ Loop: return string(s), off1, nil } -func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { +func packTxt(txt []string, msg []byte, offset int) (int, error) { if len(txt) == 0 { if offset >= len(msg) { return offset, ErrBuf @@ -458,10 +458,7 @@ func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { } var err error for _, s := range txt { - if len(s) > len(tmp) { - return offset, ErrBuf - } - offset, err = packTxtString(s, msg, offset, tmp) + offset, err = packTxtString(s, msg, offset) if err != nil { return offset, err } @@ -469,32 +466,30 @@ func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } -func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { +func packTxtString(s string, msg []byte, offset int) (int, error) { lenByteOffset := offset - if offset >= len(msg) || len(s) > len(tmp) { + if offset >= len(msg) || len(s) > 256*4+1 /* If all \DDD */ { return offset, ErrBuf } offset++ - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { + for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } - if bs[i] == '\\' { + if s[i] == '\\' { i++ - if i == len(bs) { + if i == len(s) { break } // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) + if isDDD(s[i:]) { + msg[offset] = dddToByte(s[i:]) i += 2 } else { - msg[offset] = bs[i] + msg[offset] = s[i] } } else { - msg[offset] = bs[i] + msg[offset] = s[i] } offset++ } @@ -506,30 +501,28 @@ func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } -func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { - if offset >= len(msg) || len(s) > len(tmp) { +func packOctetString(s string, msg []byte, offset int) (int, error) { + if offset >= len(msg) || len(s) > 256*4+1 { return offset, ErrBuf } - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { + for i := 0; i < len(s); i++ { if len(msg) <= offset { return offset, ErrBuf } - if bs[i] == '\\' { + if s[i] == '\\' { i++ - if i == len(bs) { + if i == len(s) { break } // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) + if isDDD(s[i:]) { + msg[offset] = dddToByte(s[i:]) i += 2 } else { - msg[offset] = bs[i] + msg[offset] = s[i] } } else { - msg[offset] = bs[i] + msg[offset] = s[i] } offset++ } @@ -551,12 +544,11 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { // Helpers for dealing with escaped bytes func isDigit(b byte) bool { return b >= '0' && b <= '9' } -func dddToByte(s []byte) byte { - _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 - return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) +func isDDD[T ~[]byte | ~string](s T) bool { + return len(s) >= 3 && isDigit(s[0]) && isDigit(s[1]) && isDigit(s[2]) } -func dddStringToByte(s string) byte { +func dddToByte[T ~[]byte | ~string](s T) byte { _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } @@ -680,9 +672,9 @@ func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) // Convert a MsgHdr to a string, with dig-like headers: // -//;; opcode: QUERY, status: NOERROR, id: 48404 +// ;; opcode: QUERY, status: NOERROR, id: 48404 // -//;; flags: qr aa rd ra; +// ;; flags: qr aa rd ra; func (h *MsgHdr) String() string { if h == nil { return " MsgHdr" @@ -866,7 +858,7 @@ func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // The header counts might have been wrong so we need to update it dh.Nscount = uint16(len(dns.Ns)) if err == nil { - dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off) + dns.Extra, _, err = unpackRRslice(int(dh.Arcount), msg, off) } // The header counts might have been wrong so we need to update it dh.Arcount = uint16(len(dns.Extra)) @@ -876,11 +868,11 @@ func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { dns.Rcode |= opt.ExtendedRcode() } - if off != len(msg) { - // TODO(miek) make this an error? - // use PackOpt to let people tell how detailed the error reporting should be? - // println("dns: extra bytes in dns packet", off, "<", len(msg)) - } + // TODO(miek) make this an error? + // use PackOpt to let people tell how detailed the error reporting should be? + // if off != len(msg) { + // // println("dns: extra bytes in dns packet", off, "<", len(msg)) + // } return err } @@ -902,23 +894,38 @@ func (dns *Msg) String() string { return " MsgHdr" } s := dns.MsgHdr.String() + " " - s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " - s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " - s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " - s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "ZONE: " + strconv.Itoa(len(dns.Question)) + ", " + s += "PREREQ: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "UPDATE: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } else { + s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " + s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } opt := dns.IsEdns0() if opt != nil { // OPT PSEUDOSECTION s += opt.String() + "\n" } if len(dns.Question) > 0 { - s += "\n;; QUESTION SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; ZONE SECTION:\n" + } else { + s += "\n;; QUESTION SECTION:\n" + } for _, r := range dns.Question { s += r.String() + "\n" } } if len(dns.Answer) > 0 { - s += "\n;; ANSWER SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; PREREQUISITE SECTION:\n" + } else { + s += "\n;; ANSWER SECTION:\n" + } for _, r := range dns.Answer { if r != nil { s += r.String() + "\n" @@ -926,7 +933,11 @@ func (dns *Msg) String() string { } } if len(dns.Ns) > 0 { - s += "\n;; AUTHORITY SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; UPDATE SECTION:\n" + } else { + s += "\n;; AUTHORITY SECTION:\n" + } for _, r := range dns.Ns { if r != nil { s += r.String() + "\n" @@ -1024,7 +1035,7 @@ func escapedNameLen(s string) int { continue } - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + if isDDD(s[i+1:]) { nameLen -= 3 i += 3 } else { @@ -1065,8 +1076,8 @@ func (dns *Msg) CopyTo(r1 *Msg) *Msg { r1.Compress = dns.Compress if len(dns.Question) > 0 { - r1.Question = make([]Question, len(dns.Question)) - copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy + // TODO(miek): Question is an immutable value, ok to do a shallow-copy + r1.Question = cloneSlice(dns.Question) } rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index ea2035cd..acec21f7 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -20,9 +20,7 @@ func unpackDataA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv4len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking a"} } - a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...) - off += net.IPv4len - return a, off, nil + return cloneSlice(msg[off : off+net.IPv4len]), off + net.IPv4len, nil } func packDataA(a net.IP, msg []byte, off int) (int, error) { @@ -47,9 +45,7 @@ func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) { if off+net.IPv6len > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking aaaa"} } - aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...) - off += net.IPv6len - return aaaa, off, nil + return cloneSlice(msg[off : off+net.IPv6len]), off + net.IPv6len, nil } func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) { @@ -299,8 +295,7 @@ func unpackString(msg []byte, off int) (string, int, error) { } func packString(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packTxtString(s, msg, off, txtTmp) + off, err := packTxtString(s, msg, off) if err != nil { return len(msg), err } @@ -402,8 +397,7 @@ func unpackStringTxt(msg []byte, off int) ([]string, int, error) { } func packStringTxt(s []string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many. - off, err := packTxt(s, msg, off, txtTmp) + off, err := packTxt(s, msg, off) if err != nil { return len(msg), err } @@ -412,29 +406,24 @@ func packStringTxt(s []string, msg []byte, off int) (int, error) { func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) { var edns []EDNS0 -Option: - var code uint16 - if off+4 > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - code = binary.BigEndian.Uint16(msg[off:]) - off += 2 - optlen := binary.BigEndian.Uint16(msg[off:]) - off += 2 - if off+int(optlen) > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - e := makeDataOpt(code) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - - if off < len(msg) { - goto Option + for off < len(msg) { + if off+4 > len(msg) { + return nil, len(msg), &Error{err: "overflow unpacking opt"} + } + code := binary.BigEndian.Uint16(msg[off:]) + off += 2 + optlen := binary.BigEndian.Uint16(msg[off:]) + off += 2 + if off+int(optlen) > len(msg) { + return nil, len(msg), &Error{err: "overflow unpacking opt"} + } + opt := makeDataOpt(code) + if err := opt.unpack(msg[off : off+int(optlen)]); err != nil { + return nil, len(msg), err + } + edns = append(edns, opt) + off += int(optlen) } - return edns, off, nil } @@ -463,8 +452,7 @@ func unpackStringOctet(msg []byte, off int) (string, int, error) { } func packStringOctet(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packOctetString(s, msg, off, txtTmp) + off, err := packOctetString(s, msg, off) if err != nil { return len(msg), err } @@ -625,7 +613,7 @@ func unpackDataSVCB(msg []byte, off int) ([]SVCBKeyValue, int, error) { } func packDataSVCB(pairs []SVCBKeyValue, msg []byte, off int) (int, error) { - pairs = append([]SVCBKeyValue(nil), pairs...) + pairs = cloneSlice(pairs) sort.Slice(pairs, func(i, j int) bool { return pairs[i].Key() < pairs[j].Key() }) @@ -810,3 +798,37 @@ func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) { Network: ipnet, }, off, nil } + +func unpackIPSECGateway(msg []byte, off int, gatewayType uint8) (net.IP, string, int, error) { + var retAddr net.IP + var retString string + var err error + + switch gatewayType { + case IPSECGatewayNone: // do nothing + case IPSECGatewayIPv4: + retAddr, off, err = unpackDataA(msg, off) + case IPSECGatewayIPv6: + retAddr, off, err = unpackDataAAAA(msg, off) + case IPSECGatewayHost: + retString, off, err = UnpackDomainName(msg, off) + } + + return retAddr, retString, off, err +} + +func packIPSECGateway(gatewayAddr net.IP, gatewayString string, msg []byte, off int, gatewayType uint8, compression compressionMap, compress bool) (int, error) { + var err error + + switch gatewayType { + case IPSECGatewayNone: // do nothing + case IPSECGatewayIPv4: + off, err = packDataA(gatewayAddr, msg, off) + case IPSECGatewayIPv6: + off, err = packDataAAAA(gatewayAddr, msg, off) + case IPSECGatewayHost: + off, err = packDomainName(gatewayString, msg, off, compression, compress) + } + + return off, err +} diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index 57be9882..062d8ff3 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -10,13 +10,13 @@ import ( "strings" ) -const maxTok = 2048 // Largest token we can return. +const maxTok = 512 // Token buffer start size, and growth size amount. // The maximum depth of $INCLUDE directives supported by the // ZoneParser API. const maxIncludeDepth = 7 -// Tokinize a RFC 1035 zone file. The tokenizer will normalize it: +// Tokenize a RFC 1035 zone file. The tokenizer will normalize it: // * Add ownernames if they are left blank; // * Suppress sequences of spaces; // * Make each RR fit on one line (_NEWLINE is send as last) @@ -605,8 +605,6 @@ func (zp *ZoneParser) Next() (RR, bool) { if !isPrivate && zp.c.Peek().token == "" { // This is a dynamic update rr. - // TODO(tmthrgd): Previously slurpRemainder was only called - // for certain RR types, which may have been important. if err := slurpRemainder(zp.c); err != nil { return zp.setParseError(err.err, err.lex) } @@ -765,8 +763,8 @@ func (zl *zlexer) Next() (lex, bool) { } var ( - str [maxTok]byte // Hold string text - com [maxTok]byte // Hold comment text + str = make([]byte, maxTok) // Hold string text + com = make([]byte, maxTok) // Hold comment text stri int // Offset in str (0 means empty) comi int // Offset in com (0 means empty) @@ -785,14 +783,12 @@ func (zl *zlexer) Next() (lex, bool) { l.line, l.column = zl.line, zl.column if stri >= len(str) { - l.token = "token length insufficient for parsing" - l.err = true - return *l, true + // if buffer length is insufficient, increase it. + str = append(str[:], make([]byte, maxTok)...) } if comi >= len(com) { - l.token = "comment length insufficient for parsing" - l.err = true - return *l, true + // if buffer length is insufficient, increase it. + com = append(com[:], make([]byte, maxTok)...) } switch x { @@ -816,7 +812,7 @@ func (zl *zlexer) Next() (lex, bool) { if stri == 0 { // Space directly in the beginning, handled in the grammar } else if zl.owner { - // If we have a string and its the first, make it an owner + // If we have a string and it's the first, make it an owner l.value = zOwner l.token = string(str[:stri]) @@ -1218,42 +1214,34 @@ func stringToCm(token string) (e, m uint8, ok bool) { if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' { token = token[0 : len(token)-1] } - s := strings.SplitN(token, ".", 2) - var meters, cmeters, val int - var err error - switch len(s) { - case 2: - if cmeters, err = strconv.Atoi(s[1]); err != nil { - return - } + + var ( + meters, cmeters, val int + err error + ) + mStr, cmStr, hasCM := strings.Cut(token, ".") + if hasCM { // There's no point in having more than 2 digits in this part, and would rather make the implementation complicated ('123' should be treated as '12'). // So we simply reject it. // We also make sure the first character is a digit to reject '+-' signs. - if len(s[1]) > 2 || s[1][0] < '0' || s[1][0] > '9' { + cmeters, err = strconv.Atoi(cmStr) + if err != nil || len(cmStr) > 2 || cmStr[0] < '0' || cmStr[0] > '9' { return } - if len(s[1]) == 1 { + if len(cmStr) == 1 { // 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm. cmeters *= 10 } - if s[0] == "" { - // This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). - break - } - fallthrough - case 1: - if meters, err = strconv.Atoi(s[0]); err != nil { - return - } + } + // This slighly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). + if !hasCM || mStr != "" { + meters, err = strconv.Atoi(mStr) // RFC1876 states the max value is 90000000.00. The latter two conditions enforce it. - if s[0][0] < '0' || s[0][0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) { + if err != nil || mStr[0] < '0' || mStr[0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) { return } - case 0: - // huh? - return 0, 0, false } - ok = true + if meters > 0 { e = 2 val = meters @@ -1265,8 +1253,7 @@ func stringToCm(token string) (e, m uint8, ok bool) { e++ val /= 10 } - m = uint8(val) - return + return e, uint8(val), true } func toAbsoluteName(name, origin string) (absolute string, ok bool) { diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index e398484d..a635e1c5 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -1,8 +1,8 @@ package dns import ( - "bytes" "encoding/base64" + "errors" "net" "strconv" "strings" @@ -11,15 +11,15 @@ import ( // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error func endingToString(c *zlexer, errstr string) (string, *ParseError) { - var buffer bytes.Buffer + var s strings.Builder l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { - return buffer.String(), &ParseError{"", errstr, l} + return s.String(), &ParseError{"", errstr, l} } switch l.value { case zString: - buffer.WriteString(l.token) + s.WriteString(l.token) case zBlank: // Ok default: return "", &ParseError{"", errstr, l} @@ -27,7 +27,7 @@ func endingToString(c *zlexer, errstr string) (string, *ParseError) { l, _ = c.Next() } - return buffer.String(), nil + return s.String(), nil } // A remainder of the rdata with embedded spaces, split on unquoted whitespace @@ -903,11 +903,18 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + if l.err { return &ParseError{"", "bad RRSIG Algorithm", l} } - rr.Algorithm = uint8(i) + i, e := strconv.ParseUint(l.token, 10, 8) + rr.Algorithm = uint8(i) // if 0 we'll check the mnemonic in the if + if e != nil { + v, ok := StringToAlgorithm[l.token] + if !ok { + return &ParseError{"", "bad RRSIG Algorithm", l} + } + rr.Algorithm = v + } c.Next() // zBlank l, _ = c.Next() @@ -1216,6 +1223,117 @@ func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") } func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") } +func (rr *IPSECKEY) parse(c *zlexer, o string) *ParseError { + l, _ := c.Next() + num, err := strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad IPSECKEY value", l} + } + rr.Precedence = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad IPSECKEY value", l} + } + rr.GatewayType = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad IPSECKEY value", l} + } + rr.Algorithm = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err { + return &ParseError{"", "bad IPSECKEY gateway", l} + } + + rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType) + if err != nil { + return &ParseError{"", "IPSECKEY " + err.Error(), l} + } + + c.Next() // zBlank + + s, pErr := endingToString(c, "bad IPSECKEY PublicKey") + if pErr != nil { + return pErr + } + rr.PublicKey = s + return slurpRemainder(c) +} + +func (rr *AMTRELAY) parse(c *zlexer, o string) *ParseError { + l, _ := c.Next() + num, err := strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad AMTRELAY value", l} + } + rr.Precedence = uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err || !(l.token == "0" || l.token == "1") { + return &ParseError{"", "bad discovery value", l} + } + if l.token == "1" { + rr.GatewayType = 0x80 + } + + c.Next() // zBlank + + l, _ = c.Next() + num, err = strconv.ParseUint(l.token, 10, 8) + if err != nil || l.err { + return &ParseError{"", "bad AMTRELAY value", l} + } + rr.GatewayType |= uint8(num) + c.Next() // zBlank + + l, _ = c.Next() + if l.err { + return &ParseError{"", "bad AMTRELAY gateway", l} + } + + rr.GatewayAddr, rr.GatewayHost, err = parseAddrHostUnion(l.token, o, rr.GatewayType&0x7f) + if err != nil { + return &ParseError{"", "AMTRELAY " + err.Error(), l} + } + + return slurpRemainder(c) +} + +// same constants and parsing between IPSECKEY and AMTRELAY +func parseAddrHostUnion(token, o string, gatewayType uint8) (addr net.IP, host string, err error) { + switch gatewayType { + case IPSECGatewayNone: + if token != "." { + return addr, host, errors.New("gateway type none with gateway set") + } + case IPSECGatewayIPv4, IPSECGatewayIPv6: + addr = net.ParseIP(token) + if addr == nil { + return addr, host, errors.New("gateway IP invalid") + } + if (addr.To4() == nil) == (gatewayType == IPSECGatewayIPv4) { + return addr, host, errors.New("gateway IP family mismatch") + } + case IPSECGatewayHost: + var ok bool + host, ok = toAbsoluteName(token, o) + if !ok { + return addr, host, errors.New("invalid gateway host") + } + } + + return addr, host, nil +} + func (rr *RKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 4e5a9aa8..64e38854 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -18,7 +18,7 @@ import ( const maxTCPQueries = 128 // aLongTimeAgo is a non-zero time, far in the past, used for -// immediate cancelation of network operations. +// immediate cancellation of network operations. var aLongTimeAgo = time.Unix(1, 0) // Handler is implemented by any value that implements ServeDNS. @@ -224,7 +224,7 @@ type Server struct { // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1). MaxTCPQueries int // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. - // It is only supported on go1.11+ and when using ListenAndServe. + // It is only supported on certain GOOSes and when using ListenAndServe. ReusePort bool // AcceptMsgFunc will check the incoming message and will reject it early in the process. // By default DefaultMsgAcceptFunc will be used. diff --git a/vendor/github.com/miekg/dns/singleinflight.go b/vendor/github.com/miekg/dns/singleinflight.go deleted file mode 100644 index febcc300..00000000 --- a/vendor/github.com/miekg/dns/singleinflight.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Adapted for dns package usage by Miek Gieben. - -package dns - -import "sync" -import "time" - -// call is an in-flight or completed singleflight.Do call -type call struct { - wg sync.WaitGroup - val *Msg - rtt time.Duration - err error - dups int -} - -// singleflight represents a class of work and forms a namespace in -// which units of work can be executed with duplicate suppression. -type singleflight struct { - sync.Mutex // protects m - m map[string]*call // lazily initialized - - dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges -} - -// Do executes and returns the results of the given function, making -// sure that only one execution is in-flight for a given key at a -// time. If a duplicate comes in, the duplicate caller waits for the -// original to complete and receives the same results. -// The return value shared indicates whether v was given to multiple callers. -func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) { - g.Lock() - if g.m == nil { - g.m = make(map[string]*call) - } - if c, ok := g.m[key]; ok { - c.dups++ - g.Unlock() - c.wg.Wait() - return c.val, c.rtt, c.err, true - } - c := new(call) - c.wg.Add(1) - g.m[key] = c - g.Unlock() - - c.val, c.rtt, c.err = fn() - c.wg.Done() - - if !g.dontDeleteForTesting { - g.Lock() - delete(g.m, key) - g.Unlock() - } - - return c.val, c.rtt, c.err, c.dups > 0 -} diff --git a/vendor/github.com/miekg/dns/svcb.go b/vendor/github.com/miekg/dns/svcb.go index ea58710d..d38aa2f0 100644 --- a/vendor/github.com/miekg/dns/svcb.go +++ b/vendor/github.com/miekg/dns/svcb.go @@ -289,7 +289,7 @@ func (s *SVCBMandatory) String() string { } func (s *SVCBMandatory) pack() ([]byte, error) { - codes := append([]SVCBKey(nil), s.Code...) + codes := cloneSlice(s.Code) sort.Slice(codes, func(i, j int) bool { return codes[i] < codes[j] }) @@ -314,10 +314,11 @@ func (s *SVCBMandatory) unpack(b []byte) error { } func (s *SVCBMandatory) parse(b string) error { - str := strings.Split(b, ",") - codes := make([]SVCBKey, 0, len(str)) - for _, e := range str { - codes = append(codes, svcbStringToKey(e)) + codes := make([]SVCBKey, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var key string + key, b, _ = strings.Cut(b, ",") + codes = append(codes, svcbStringToKey(key)) } s.Code = codes return nil @@ -328,9 +329,7 @@ func (s *SVCBMandatory) len() int { } func (s *SVCBMandatory) copy() SVCBKeyValue { - return &SVCBMandatory{ - append([]SVCBKey(nil), s.Code...), - } + return &SVCBMandatory{cloneSlice(s.Code)} } // SVCBAlpn pair is used to list supported connection protocols. @@ -353,7 +352,7 @@ func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN } func (s *SVCBAlpn) String() string { // An ALPN value is a comma-separated list of values, each of which can be // an arbitrary binary value. In order to allow parsing, the comma and - // backslash characters are themselves excaped. + // backslash characters are themselves escaped. // // However, this escaping is done in addition to the normal escaping which // happens in zone files, meaning that these values must be @@ -481,9 +480,7 @@ func (s *SVCBAlpn) len() int { } func (s *SVCBAlpn) copy() SVCBKeyValue { - return &SVCBAlpn{ - append([]string(nil), s.Alpn...), - } + return &SVCBAlpn{cloneSlice(s.Alpn)} } // SVCBNoDefaultAlpn pair signifies no support for default connection protocols. @@ -563,15 +560,15 @@ func (s *SVCBPort) parse(b string) error { // to the hinted IP address may be terminated and a new connection may be opened. // Basic use pattern for creating an ipv4hint option: // -// h := new(dns.HTTPS) -// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} -// e := new(dns.SVCBIPv4Hint) -// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} +// h := new(dns.HTTPS) +// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET} +// e := new(dns.SVCBIPv4Hint) +// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()} // -// Or +// Or // -// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} -// h.Value = append(h.Value, e) +// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()} +// h.Value = append(h.Value, e) type SVCBIPv4Hint struct { Hint []net.IP } @@ -595,6 +592,7 @@ func (s *SVCBIPv4Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%4 != 0 { return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4") } + b = cloneSlice(b) x := make([]net.IP, 0, len(b)/4) for i := 0; i < len(b); i += 4 { x = append(x, net.IP(b[i:i+4])) @@ -616,31 +614,33 @@ func (s *SVCBIPv4Hint) String() string { } func (s *SVCBIPv4Hint) parse(b string) error { + if b == "" { + return errors.New("dns: svcbipv4hint: empty hint") + } if strings.Contains(b, ":") { return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6") } - str := strings.Split(b, ",") - dst := make([]net.IP, len(str)) - for i, e := range str { + + hint := make([]net.IP, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var e string + e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e).To4() if ip == nil { return errors.New("dns: svcbipv4hint: bad ip") } - dst[i] = ip + hint = append(hint, ip) } - s.Hint = dst + s.Hint = hint return nil } func (s *SVCBIPv4Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { - hint[i] = copyIP(ip) - } - - return &SVCBIPv4Hint{ - Hint: hint, + hint[i] = cloneSlice(ip) } + return &SVCBIPv4Hint{Hint: hint} } // SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx]. @@ -660,19 +660,18 @@ func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) } func (s *SVCBECHConfig) len() int { return len(s.ECH) } func (s *SVCBECHConfig) pack() ([]byte, error) { - return append([]byte(nil), s.ECH...), nil + return cloneSlice(s.ECH), nil } func (s *SVCBECHConfig) copy() SVCBKeyValue { - return &SVCBECHConfig{ - append([]byte(nil), s.ECH...), - } + return &SVCBECHConfig{cloneSlice(s.ECH)} } func (s *SVCBECHConfig) unpack(b []byte) error { - s.ECH = append([]byte(nil), b...) + s.ECH = cloneSlice(b) return nil } + func (s *SVCBECHConfig) parse(b string) error { x, err := fromBase64([]byte(b)) if err != nil { @@ -715,6 +714,7 @@ func (s *SVCBIPv6Hint) unpack(b []byte) error { if len(b) == 0 || len(b)%16 != 0 { return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16") } + b = cloneSlice(b) x := make([]net.IP, 0, len(b)/16) for i := 0; i < len(b); i += 16 { ip := net.IP(b[i : i+16]) @@ -739,9 +739,14 @@ func (s *SVCBIPv6Hint) String() string { } func (s *SVCBIPv6Hint) parse(b string) error { - str := strings.Split(b, ",") - dst := make([]net.IP, len(str)) - for i, e := range str { + if b == "" { + return errors.New("dns: svcbipv6hint: empty hint") + } + + hint := make([]net.IP, 0, strings.Count(b, ",")+1) + for len(b) > 0 { + var e string + e, b, _ = strings.Cut(b, ",") ip := net.ParseIP(e) if ip == nil { return errors.New("dns: svcbipv6hint: bad ip") @@ -749,21 +754,18 @@ func (s *SVCBIPv6Hint) parse(b string) error { if ip.To4() != nil { return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6") } - dst[i] = ip + hint = append(hint, ip) } - s.Hint = dst + s.Hint = hint return nil } func (s *SVCBIPv6Hint) copy() SVCBKeyValue { hint := make([]net.IP, len(s.Hint)) for i, ip := range s.Hint { - hint[i] = copyIP(ip) - } - - return &SVCBIPv6Hint{ - Hint: hint, + hint[i] = cloneSlice(ip) } + return &SVCBIPv6Hint{Hint: hint} } // SVCBDoHPath pair is used to indicate the URI template that the @@ -831,11 +833,11 @@ type SVCBLocal struct { func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode } func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) } -func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil } +func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil } func (s *SVCBLocal) len() int { return len(s.Data) } func (s *SVCBLocal) unpack(b []byte) error { - s.Data = append([]byte(nil), b...) + s.Data = cloneSlice(b) return nil } @@ -849,9 +851,7 @@ func (s *SVCBLocal) parse(b string) error { } func (s *SVCBLocal) copy() SVCBKeyValue { - return &SVCBLocal{s.KeyCode, - append([]byte(nil), s.Data...), - } + return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)} } func (rr *SVCB) String() string { @@ -867,8 +867,8 @@ func (rr *SVCB) String() string { // areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their // copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function. func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool { - a = append([]SVCBKeyValue(nil), a...) - b = append([]SVCBKeyValue(nil), b...) + a = cloneSlice(a) + b = cloneSlice(b) sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() }) sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() }) for i, e := range a { diff --git a/vendor/github.com/miekg/dns/tools.go b/vendor/github.com/miekg/dns/tools.go index d1118253..ccf8f6bf 100644 --- a/vendor/github.com/miekg/dns/tools.go +++ b/vendor/github.com/miekg/dns/tools.go @@ -1,3 +1,4 @@ +//go:build tools // +build tools // We include our tool dependencies for `go generate` here to ensure they're diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index d9becb67..c9a03dec 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -65,6 +65,7 @@ const ( TypeAPL uint16 = 42 TypeDS uint16 = 43 TypeSSHFP uint16 = 44 + TypeIPSECKEY uint16 = 45 TypeRRSIG uint16 = 46 TypeNSEC uint16 = 47 TypeDNSKEY uint16 = 48 @@ -98,6 +99,7 @@ const ( TypeURI uint16 = 256 TypeCAA uint16 = 257 TypeAVC uint16 = 258 + TypeAMTRELAY uint16 = 260 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 @@ -159,6 +161,22 @@ const ( ZoneMDHashAlgSHA512 = 2 ) +// Used in IPSEC https://datatracker.ietf.org/doc/html/rfc4025#section-2.3 +const ( + IPSECGatewayNone uint8 = iota + IPSECGatewayIPv4 + IPSECGatewayIPv6 + IPSECGatewayHost +) + +// Used in AMTRELAY https://datatracker.ietf.org/doc/html/rfc8777#section-4.2.3 +const ( + AMTRELAYNone = IPSECGatewayNone + AMTRELAYIPv4 = IPSECGatewayIPv4 + AMTRELAYIPv6 = IPSECGatewayIPv6 + AMTRELAYHost = IPSECGatewayHost +) + // Header is the wire format for the DNS packet header. type Header struct { Id uint16 @@ -180,7 +198,7 @@ const ( _CD = 1 << 4 // checking disabled ) -// Various constants used in the LOC RR. See RFC 1887. +// Various constants used in the LOC RR. See RFC 1876. const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. @@ -218,6 +236,9 @@ var CertTypeToString = map[uint16]string{ CertOID: "OID", } +// Prefix for IPv4 encoded as IPv6 address +const ipv4InIPv6Prefix = "::ffff:" + //go:generate go run types_generate.go // Question holds a DNS question. Usually there is just one. While the @@ -613,8 +634,8 @@ func nextByte(s string, offset int) (byte, int) { return 0, 0 case 2, 3: // too short to be \ddd default: // maybe \ddd - if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) { - return dddStringToByte(s[offset+1:]), 4 + if isDDD(s[offset+1:]) { + return dddToByte(s[offset+1:]), 4 } } // not \ddd, just an RFC 1035 "quoted" character @@ -733,6 +754,11 @@ func (rr *AAAA) String() string { if rr.AAAA == nil { return rr.Hdr.String() } + + if rr.AAAA.To4() != nil { + return rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String() + } + return rr.Hdr.String() + rr.AAAA.String() } @@ -774,7 +800,10 @@ type LOC struct { // cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent // format and returns a string in m (two decimals for the cm). -func cmToM(m, e uint8) string { +func cmToM(x uint8) string { + m := x & 0xf0 >> 4 + e := x & 0x0f + if e < 2 { if e == 1 { m *= 10 @@ -830,10 +859,9 @@ func (rr *LOC) String() string { s += fmt.Sprintf("%.0fm ", alt) } - s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m " - s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m " - s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m" - + s += cmToM(rr.Size) + "m " + s += cmToM(rr.HorizPre) + "m " + s += cmToM(rr.VertPre) + "m" return s } @@ -994,6 +1022,69 @@ func (rr *DNSKEY) String() string { " " + rr.PublicKey } +// IPSECKEY RR. See RFC 4025. +type IPSECKEY struct { + Hdr RR_Header + Precedence uint8 + GatewayType uint8 + Algorithm uint8 + GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost + GatewayHost string `dns:"ipsechost"` + PublicKey string `dns:"base64"` +} + +func (rr *IPSECKEY) String() string { + var gateway string + switch rr.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + gateway = rr.GatewayAddr.String() + case IPSECGatewayHost: + gateway = rr.GatewayHost + case IPSECGatewayNone: + fallthrough + default: + gateway = "." + } + + return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + + " " + strconv.Itoa(int(rr.GatewayType)) + + " " + strconv.Itoa(int(rr.Algorithm)) + + " " + gateway + + " " + rr.PublicKey +} + +// AMTRELAY RR. See RFC 8777. +type AMTRELAY struct { + Hdr RR_Header + Precedence uint8 + GatewayType uint8 // discovery is packed in here at bit 0x80 + GatewayAddr net.IP `dns:"-"` // packing/unpacking/parsing/etc handled together with GatewayHost + GatewayHost string `dns:"amtrelayhost"` +} + +func (rr *AMTRELAY) String() string { + var gateway string + switch rr.GatewayType & 0x7f { + case AMTRELAYIPv4, AMTRELAYIPv6: + gateway = rr.GatewayAddr.String() + case AMTRELAYHost: + gateway = rr.GatewayHost + case AMTRELAYNone: + fallthrough + default: + gateway = "." + } + boolS := "0" + if rr.GatewayType&0x80 == 0x80 { + boolS = "1" + } + + return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + + " " + boolS + + " " + strconv.Itoa(int(rr.GatewayType&0x7f)) + + " " + gateway +} + // RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template. type RKEY struct { Hdr RR_Header @@ -1434,7 +1525,7 @@ func (a *APLPrefix) str() string { case net.IPv6len: // add prefix for IPv4-mapped IPv6 if v4 := a.Network.IP.To4(); v4 != nil { - sb.WriteString("::ffff:") + sb.WriteString(ipv4InIPv6Prefix) } sb.WriteString(a.Network.IP.String()) } @@ -1450,7 +1541,7 @@ func (a *APLPrefix) str() string { // equals reports whether two APL prefixes are identical. func (a *APLPrefix) equals(b *APLPrefix) bool { return a.Negation == b.Negation && - bytes.Equal(a.Network.IP, b.Network.IP) && + a.Network.IP.Equal(b.Network.IP) && bytes.Equal(a.Network.Mask, b.Network.Mask) } @@ -1518,21 +1609,19 @@ func euiToString(eui uint64, bits int) (hex string) { return } -// copyIP returns a copy of ip. -func copyIP(ip net.IP) net.IP { - p := make(net.IP, len(ip)) - copy(p, ip) - return p +// cloneSlice returns a shallow copy of s. +func cloneSlice[E any, S ~[]E](s S) S { + if s == nil { + return nil + } + return append(S(nil), s...) } // copyNet returns a copy of a subnet. func copyNet(n net.IPNet) net.IPNet { - m := make(net.IPMask, len(n.Mask)) - copy(m, n.Mask) - return net.IPNet{ - IP: copyIP(n.IP), - Mask: m, + IP: cloneSlice(n.IP), + Mask: cloneSlice(n.Mask), } } diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go index a4826ee2..c018ad43 100644 --- a/vendor/github.com/miekg/dns/udp.go +++ b/vendor/github.com/miekg/dns/udp.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package dns diff --git a/vendor/github.com/miekg/dns/udp_windows.go b/vendor/github.com/miekg/dns/udp_windows.go index e7dd8ca3..a259b67e 100644 --- a/vendor/github.com/miekg/dns/udp_windows.go +++ b/vendor/github.com/miekg/dns/udp_windows.go @@ -1,5 +1,9 @@ +//go:build windows // +build windows +// TODO(tmthrgd): Remove this Windows-specific code if go.dev/issue/7175 and +// go.dev/issue/7174 are ever fixed. + package dns import "net" @@ -14,7 +18,6 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. -// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP. func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { n, raddr, err := conn.ReadFrom(b) if err != nil { @@ -24,12 +27,9 @@ func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { } // WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. -// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP. func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { return conn.WriteTo(b, session.raddr) } -// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods -// use the standard method in udp.go for these. func setUDPSocketOptions(*net.UDPConn) error { return nil } func parseDstFromOOB([]byte, net.IP) net.IP { return nil } diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index b1a872bd..a0911366 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 50} +var Version = v{1, 1, 56} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index 1917e91c..05b3c5ad 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -44,7 +44,6 @@ func (t *Transfer) tsigProvider() TsigProvider { // dnscon := &dns.Conn{Conn:con} // transfer = &dns.Transfer{Conn: dnscon} // channel, err := transfer.In(message, master) -// func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: @@ -81,8 +80,13 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { first := true - defer t.Close() - defer close(c) + defer func() { + // First close the connection, then the channel. This allows functions blocked on + // the channel to assume that the connection is closed and no further operations are + // pending when they resume. + t.Close() + close(c) + }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout @@ -132,8 +136,13 @@ func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { axfr := true n := 0 qser := q.Ns[0].(*SOA).Serial - defer t.Close() - defer close(c) + defer func() { + // First close the connection, then the channel. This allows functions blocked on + // the channel to assume that the connection is closed and no further operations are + // pending when they resume. + t.Close() + close(c) + }() timeout := dnsTimeout if t.ReadTimeout != 0 { timeout = t.ReadTimeout diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go index 9eb1dac2..450bbbc2 100644 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -43,6 +43,32 @@ func (r1 *AFSDB) isDuplicate(_r2 RR) bool { return true } +func (r1 *AMTRELAY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*AMTRELAY) + if !ok { + return false + } + _ = r2 + if r1.Precedence != r2.Precedence { + return false + } + if r1.GatewayType != r2.GatewayType { + return false + } + switch r1.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + if !r1.GatewayAddr.Equal(r2.GatewayAddr) { + return false + } + case IPSECGatewayHost: + if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { + return false + } + } + + return true +} + func (r1 *ANY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*ANY) if !ok { @@ -423,6 +449,38 @@ func (r1 *HTTPS) isDuplicate(_r2 RR) bool { return true } +func (r1 *IPSECKEY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*IPSECKEY) + if !ok { + return false + } + _ = r2 + if r1.Precedence != r2.Precedence { + return false + } + if r1.GatewayType != r2.GatewayType { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + switch r1.GatewayType { + case IPSECGatewayIPv4, IPSECGatewayIPv6: + if !r1.GatewayAddr.Equal(r2.GatewayAddr) { + return false + } + case IPSECGatewayHost: + if !isDuplicateName(r1.GatewayHost, r2.GatewayHost) { + return false + } + } + + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + func (r1 *KEY) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*KEY) if !ok { diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index fc0822f9..3ea0eb42 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -32,6 +32,22 @@ func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress return off, nil } +func (rr *AMTRELAY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packUint8(rr.Precedence, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.GatewayType, msg, off) + if err != nil { + return off, err + } + off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) + if err != nil { + return off, err + } + return off, nil +} + func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { return off, nil } @@ -332,6 +348,30 @@ func (rr *HTTPS) pack(msg []byte, off int, compression compressionMap, compress return off, nil } +func (rr *IPSECKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packUint8(rr.Precedence, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.GatewayType, msg, off) + if err != nil { + return off, err + } + off, err = packUint8(rr.Algorithm, msg, off) + if err != nil { + return off, err + } + off, err = packIPSECGateway(rr.GatewayAddr, rr.GatewayHost, msg, off, rr.GatewayType, compression, false) + if err != nil { + return off, err + } + off, err = packStringBase64(rr.PublicKey, msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packUint16(rr.Flags, msg, off) if err != nil { @@ -1180,6 +1220,34 @@ func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *AMTRELAY) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Precedence, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.GatewayType, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + if off == len(msg) { + return off, nil + } + rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) + if err != nil { + return off, err + } + return off, nil +} + func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart @@ -1636,6 +1704,48 @@ func (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *IPSECKEY) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Precedence, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.GatewayType, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.Algorithm, off, err = unpackUint8(msg, off) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + if off == len(msg) { + return off, nil + } + rr.GatewayAddr, rr.GatewayHost, off, err = unpackIPSECGateway(msg, off, rr.GatewayType) + if err != nil { + return off, err + } + if off == len(msg) { + return off, nil + } + rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) + if err != nil { + return off, err + } + return off, nil +} + func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 5d060cfe..1b6f4320 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -12,6 +12,7 @@ var TypeToRR = map[uint16]func() RR{ TypeA: func() RR { return new(A) }, TypeAAAA: func() RR { return new(AAAA) }, TypeAFSDB: func() RR { return new(AFSDB) }, + TypeAMTRELAY: func() RR { return new(AMTRELAY) }, TypeANY: func() RR { return new(ANY) }, TypeAPL: func() RR { return new(APL) }, TypeAVC: func() RR { return new(AVC) }, @@ -34,6 +35,7 @@ var TypeToRR = map[uint16]func() RR{ TypeHINFO: func() RR { return new(HINFO) }, TypeHIP: func() RR { return new(HIP) }, TypeHTTPS: func() RR { return new(HTTPS) }, + TypeIPSECKEY: func() RR { return new(IPSECKEY) }, TypeKEY: func() RR { return new(KEY) }, TypeKX: func() RR { return new(KX) }, TypeL32: func() RR { return new(L32) }, @@ -90,6 +92,7 @@ var TypeToString = map[uint16]string{ TypeA: "A", TypeAAAA: "AAAA", TypeAFSDB: "AFSDB", + TypeAMTRELAY: "AMTRELAY", TypeANY: "ANY", TypeAPL: "APL", TypeATMA: "ATMA", @@ -114,6 +117,7 @@ var TypeToString = map[uint16]string{ TypeHINFO: "HINFO", TypeHIP: "HIP", TypeHTTPS: "HTTPS", + TypeIPSECKEY: "IPSECKEY", TypeISDN: "ISDN", TypeIXFR: "IXFR", TypeKEY: "KEY", @@ -176,6 +180,7 @@ var TypeToString = map[uint16]string{ func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } +func (rr *AMTRELAY) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) Header() *RR_Header { return &rr.Hdr } func (rr *APL) Header() *RR_Header { return &rr.Hdr } func (rr *AVC) Header() *RR_Header { return &rr.Hdr } @@ -198,6 +203,7 @@ func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } func (rr *HIP) Header() *RR_Header { return &rr.Hdr } func (rr *HTTPS) Header() *RR_Header { return &rr.Hdr } +func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } func (rr *KEY) Header() *RR_Header { return &rr.Hdr } func (rr *KX) Header() *RR_Header { return &rr.Hdr } func (rr *L32) Header() *RR_Header { return &rr.Hdr } @@ -257,6 +263,7 @@ func (rr *A) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AAAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) if len(rr.AAAA) != 0 { @@ -264,16 +271,34 @@ func (rr *AAAA) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AFSDB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Subtype l += domainNameLen(rr.Hostname, off+l, compression, false) return l } + +func (rr *AMTRELAY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l++ // Precedence + l++ // GatewayType + switch rr.GatewayType { + case AMTRELAYIPv4: + l += net.IPv4len + case AMTRELAYIPv6: + l += net.IPv6len + case AMTRELAYHost: + l += len(rr.GatewayHost) + 1 + } + return l +} + func (rr *ANY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) return l } + func (rr *APL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Prefixes { @@ -281,6 +306,7 @@ func (rr *APL) len(off int, compression map[string]struct{}) int { } return l } + func (rr *AVC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -288,6 +314,7 @@ func (rr *AVC) len(off int, compression map[string]struct{}) int { } return l } + func (rr *CAA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Flag @@ -295,6 +322,7 @@ func (rr *CAA) len(off int, compression map[string]struct{}) int { l += len(rr.Value) return l } + func (rr *CERT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Type @@ -303,21 +331,25 @@ func (rr *CERT) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) return l } + func (rr *CNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, true) return l } + func (rr *DHCID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.Digest)) return l } + func (rr *DNAME) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Target, off+l, compression, false) return l } + func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags @@ -326,6 +358,7 @@ func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *DS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag @@ -334,26 +367,31 @@ func (rr *DS) len(off int, compression map[string]struct{}) int { l += len(rr.Digest) / 2 return l } + func (rr *EID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Endpoint) / 2 return l } + func (rr *EUI48) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 6 // Address return l } + func (rr *EUI64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 8 // Address return l } + func (rr *GID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Gid return l } + func (rr *GPOS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Longitude) + 1 @@ -361,12 +399,14 @@ func (rr *GPOS) len(off int, compression map[string]struct{}) int { l += len(rr.Altitude) + 1 return l } + func (rr *HINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Cpu) + 1 l += len(rr.Os) + 1 return l } + func (rr *HIP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // HitLength @@ -379,12 +419,31 @@ func (rr *HIP) len(off int, compression map[string]struct{}) int { } return l } + +func (rr *IPSECKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l++ // Precedence + l++ // GatewayType + l++ // Algorithm + switch rr.GatewayType { + case IPSECGatewayIPv4: + l += net.IPv4len + case IPSECGatewayIPv6: + l += net.IPv6len + case IPSECGatewayHost: + l += len(rr.GatewayHost) + 1 + } + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + return l +} + func (rr *KX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Exchanger, off+l, compression, false) return l } + func (rr *L32) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference @@ -393,12 +452,14 @@ func (rr *L32) len(off int, compression map[string]struct{}) int { } return l } + func (rr *L64) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // Locator64 return l } + func (rr *LOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Version @@ -410,49 +471,58 @@ func (rr *LOC) len(off int, compression map[string]struct{}) int { l += 4 // Altitude return l } + func (rr *LP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Fqdn, off+l, compression, false) return l } + func (rr *MB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mb, off+l, compression, true) return l } + func (rr *MD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Md, off+l, compression, true) return l } + func (rr *MF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mf, off+l, compression, true) return l } + func (rr *MG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mg, off+l, compression, true) return l } + func (rr *MINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Rmail, off+l, compression, true) l += domainNameLen(rr.Email, off+l, compression, true) return l } + func (rr *MR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mr, off+l, compression, true) return l } + func (rr *MX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Mx, off+l, compression, true) return l } + func (rr *NAPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Order @@ -463,17 +533,20 @@ func (rr *NAPTR) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Replacement, off+l, compression, false) return l } + func (rr *NID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // NodeID return l } + func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Locator) / 2 return l } + func (rr *NINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.ZSData { @@ -481,16 +554,19 @@ func (rr *NINFO) len(off int, compression map[string]struct{}) int { } return l } + func (rr *NS) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) return l } + func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, false) return l } + func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Hash @@ -500,21 +576,25 @@ func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { l += len(rr.Salt) / 2 return l } + func (rr *NULL) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Data) return l } + func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *PTR) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ptr, off+l, compression, true) return l } + func (rr *PX) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference @@ -522,11 +602,13 @@ func (rr *PX) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Mapx400, off+l, compression, false) return l } + func (rr *RFC3597) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Rdata) / 2 return l } + func (rr *RKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Flags @@ -535,12 +617,14 @@ func (rr *RKEY) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } + func (rr *RP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Mbox, off+l, compression, false) l += domainNameLen(rr.Txt, off+l, compression, false) return l } + func (rr *RRSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // TypeCovered @@ -554,12 +638,14 @@ func (rr *RRSIG) len(off int, compression map[string]struct{}) int { l += base64.StdEncoding.DecodedLen(len(rr.Signature)) return l } + func (rr *RT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Preference l += domainNameLen(rr.Host, off+l, compression, false) return l } + func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage @@ -568,6 +654,7 @@ func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { l += len(rr.Certificate) / 2 return l } + func (rr *SOA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Ns, off+l, compression, true) @@ -579,6 +666,7 @@ func (rr *SOA) len(off int, compression map[string]struct{}) int { l += 4 // Minttl return l } + func (rr *SPF) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -586,6 +674,7 @@ func (rr *SPF) len(off int, compression map[string]struct{}) int { } return l } + func (rr *SRV) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -594,6 +683,7 @@ func (rr *SRV) len(off int, compression map[string]struct{}) int { l += domainNameLen(rr.Target, off+l, compression, false) return l } + func (rr *SSHFP) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Algorithm @@ -601,6 +691,7 @@ func (rr *SSHFP) len(off int, compression map[string]struct{}) int { l += len(rr.FingerPrint) / 2 return l } + func (rr *SVCB) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -610,6 +701,7 @@ func (rr *SVCB) len(off int, compression map[string]struct{}) int { } return l } + func (rr *TA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // KeyTag @@ -618,12 +710,14 @@ func (rr *TA) len(off int, compression map[string]struct{}) int { l += len(rr.Digest) / 2 return l } + func (rr *TALINK) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.PreviousName, off+l, compression, false) l += domainNameLen(rr.NextName, off+l, compression, false) return l } + func (rr *TKEY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) @@ -637,6 +731,7 @@ func (rr *TKEY) len(off int, compression map[string]struct{}) int { l += len(rr.OtherData) / 2 return l } + func (rr *TLSA) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l++ // Usage @@ -645,6 +740,7 @@ func (rr *TLSA) len(off int, compression map[string]struct{}) int { l += len(rr.Certificate) / 2 return l } + func (rr *TSIG) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += domainNameLen(rr.Algorithm, off+l, compression, false) @@ -658,6 +754,7 @@ func (rr *TSIG) len(off int, compression map[string]struct{}) int { l += len(rr.OtherData) / 2 return l } + func (rr *TXT) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -665,16 +762,19 @@ func (rr *TXT) len(off int, compression map[string]struct{}) int { } return l } + func (rr *UID) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Uid return l } + func (rr *UINFO) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.Uinfo) + 1 return l } + func (rr *URI) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 2 // Priority @@ -682,11 +782,13 @@ func (rr *URI) len(off int, compression map[string]struct{}) int { l += len(rr.Target) return l } + func (rr *X25) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += len(rr.PSDNAddress) + 1 return l } + func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) l += 4 // Serial @@ -698,17 +800,31 @@ func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { // copy() functions func (rr *A) copy() RR { - return &A{rr.Hdr, copyIP(rr.A)} + return &A{rr.Hdr, cloneSlice(rr.A)} } + func (rr *AAAA) copy() RR { - return &AAAA{rr.Hdr, copyIP(rr.AAAA)} + return &AAAA{rr.Hdr, cloneSlice(rr.AAAA)} } + func (rr *AFSDB) copy() RR { return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname} } + +func (rr *AMTRELAY) copy() RR { + return &AMTRELAY{ + rr.Hdr, + rr.Precedence, + rr.GatewayType, + cloneSlice(rr.GatewayAddr), + rr.GatewayHost, + } +} + func (rr *ANY) copy() RR { return &ANY{rr.Hdr} } + func (rr *APL) copy() RR { Prefixes := make([]APLPrefix, len(rr.Prefixes)) for i, e := range rr.Prefixes { @@ -716,150 +832,270 @@ func (rr *APL) copy() RR { } return &APL{rr.Hdr, Prefixes} } + func (rr *AVC) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &AVC{rr.Hdr, Txt} + return &AVC{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *CAA) copy() RR { - return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} + return &CAA{ + rr.Hdr, + rr.Flag, + rr.Tag, + rr.Value, + } } + func (rr *CDNSKEY) copy() RR { return &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)} } + func (rr *CDS) copy() RR { return &CDS{*rr.DS.copy().(*DS)} } + func (rr *CERT) copy() RR { - return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} + return &CERT{ + rr.Hdr, + rr.Type, + rr.KeyTag, + rr.Algorithm, + rr.Certificate, + } } + func (rr *CNAME) copy() RR { return &CNAME{rr.Hdr, rr.Target} } + func (rr *CSYNC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap} + return &CSYNC{ + rr.Hdr, + rr.Serial, + rr.Flags, + cloneSlice(rr.TypeBitMap), + } } + func (rr *DHCID) copy() RR { return &DHCID{rr.Hdr, rr.Digest} } + func (rr *DLV) copy() RR { return &DLV{*rr.DS.copy().(*DS)} } + func (rr *DNAME) copy() RR { return &DNAME{rr.Hdr, rr.Target} } + func (rr *DNSKEY) copy() RR { - return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &DNSKEY{ + rr.Hdr, + rr.Flags, + rr.Protocol, + rr.Algorithm, + rr.PublicKey, + } } + func (rr *DS) copy() RR { - return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} + return &DS{ + rr.Hdr, + rr.KeyTag, + rr.Algorithm, + rr.DigestType, + rr.Digest, + } } + func (rr *EID) copy() RR { return &EID{rr.Hdr, rr.Endpoint} } + func (rr *EUI48) copy() RR { return &EUI48{rr.Hdr, rr.Address} } + func (rr *EUI64) copy() RR { return &EUI64{rr.Hdr, rr.Address} } + func (rr *GID) copy() RR { return &GID{rr.Hdr, rr.Gid} } + func (rr *GPOS) copy() RR { - return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude} + return &GPOS{ + rr.Hdr, + rr.Longitude, + rr.Latitude, + rr.Altitude, + } } + func (rr *HINFO) copy() RR { return &HINFO{rr.Hdr, rr.Cpu, rr.Os} } + func (rr *HIP) copy() RR { - RendezvousServers := make([]string, len(rr.RendezvousServers)) - copy(RendezvousServers, rr.RendezvousServers) - return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers} + return &HIP{ + rr.Hdr, + rr.HitLength, + rr.PublicKeyAlgorithm, + rr.PublicKeyLength, + rr.Hit, + rr.PublicKey, + cloneSlice(rr.RendezvousServers), + } } + func (rr *HTTPS) copy() RR { return &HTTPS{*rr.SVCB.copy().(*SVCB)} } + +func (rr *IPSECKEY) copy() RR { + return &IPSECKEY{ + rr.Hdr, + rr.Precedence, + rr.GatewayType, + rr.Algorithm, + cloneSlice(rr.GatewayAddr), + rr.GatewayHost, + rr.PublicKey, + } +} + func (rr *KEY) copy() RR { return &KEY{*rr.DNSKEY.copy().(*DNSKEY)} } + func (rr *KX) copy() RR { return &KX{rr.Hdr, rr.Preference, rr.Exchanger} } + func (rr *L32) copy() RR { - return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)} + return &L32{rr.Hdr, rr.Preference, cloneSlice(rr.Locator32)} } + func (rr *L64) copy() RR { return &L64{rr.Hdr, rr.Preference, rr.Locator64} } + func (rr *LOC) copy() RR { - return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} + return &LOC{ + rr.Hdr, + rr.Version, + rr.Size, + rr.HorizPre, + rr.VertPre, + rr.Latitude, + rr.Longitude, + rr.Altitude, + } } + func (rr *LP) copy() RR { return &LP{rr.Hdr, rr.Preference, rr.Fqdn} } + func (rr *MB) copy() RR { return &MB{rr.Hdr, rr.Mb} } + func (rr *MD) copy() RR { return &MD{rr.Hdr, rr.Md} } + func (rr *MF) copy() RR { return &MF{rr.Hdr, rr.Mf} } + func (rr *MG) copy() RR { return &MG{rr.Hdr, rr.Mg} } + func (rr *MINFO) copy() RR { return &MINFO{rr.Hdr, rr.Rmail, rr.Email} } + func (rr *MR) copy() RR { return &MR{rr.Hdr, rr.Mr} } + func (rr *MX) copy() RR { return &MX{rr.Hdr, rr.Preference, rr.Mx} } + func (rr *NAPTR) copy() RR { - return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement} + return &NAPTR{ + rr.Hdr, + rr.Order, + rr.Preference, + rr.Flags, + rr.Service, + rr.Regexp, + rr.Replacement, + } } + func (rr *NID) copy() RR { return &NID{rr.Hdr, rr.Preference, rr.NodeID} } + func (rr *NIMLOC) copy() RR { return &NIMLOC{rr.Hdr, rr.Locator} } + func (rr *NINFO) copy() RR { - ZSData := make([]string, len(rr.ZSData)) - copy(ZSData, rr.ZSData) - return &NINFO{rr.Hdr, ZSData} + return &NINFO{rr.Hdr, cloneSlice(rr.ZSData)} } + func (rr *NS) copy() RR { return &NS{rr.Hdr, rr.Ns} } + func (rr *NSAPPTR) copy() RR { return &NSAPPTR{rr.Hdr, rr.Ptr} } + func (rr *NSEC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap} + return &NSEC{rr.Hdr, rr.NextDomain, cloneSlice(rr.TypeBitMap)} } + func (rr *NSEC3) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC3{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap} + return &NSEC3{ + rr.Hdr, + rr.Hash, + rr.Flags, + rr.Iterations, + rr.SaltLength, + rr.Salt, + rr.HashLength, + rr.NextDomain, + cloneSlice(rr.TypeBitMap), + } } + func (rr *NSEC3PARAM) copy() RR { - return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} + return &NSEC3PARAM{ + rr.Hdr, + rr.Hash, + rr.Flags, + rr.Iterations, + rr.SaltLength, + rr.Salt, + } } + func (rr *NULL) copy() RR { return &NULL{rr.Hdr, rr.Data} } + func (rr *OPENPGPKEY) copy() RR { return &OPENPGPKEY{rr.Hdr, rr.PublicKey} } + func (rr *OPT) copy() RR { Option := make([]EDNS0, len(rr.Option)) for i, e := range rr.Option { @@ -867,86 +1103,205 @@ func (rr *OPT) copy() RR { } return &OPT{rr.Hdr, Option} } + func (rr *PTR) copy() RR { return &PTR{rr.Hdr, rr.Ptr} } + func (rr *PX) copy() RR { - return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400} + return &PX{ + rr.Hdr, + rr.Preference, + rr.Map822, + rr.Mapx400, + } } + func (rr *RFC3597) copy() RR { return &RFC3597{rr.Hdr, rr.Rdata} } + func (rr *RKEY) copy() RR { - return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &RKEY{ + rr.Hdr, + rr.Flags, + rr.Protocol, + rr.Algorithm, + rr.PublicKey, + } } + func (rr *RP) copy() RR { return &RP{rr.Hdr, rr.Mbox, rr.Txt} } + func (rr *RRSIG) copy() RR { - return &RRSIG{rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature} + return &RRSIG{ + rr.Hdr, + rr.TypeCovered, + rr.Algorithm, + rr.Labels, + rr.OrigTtl, + rr.Expiration, + rr.Inception, + rr.KeyTag, + rr.SignerName, + rr.Signature, + } } + func (rr *RT) copy() RR { return &RT{rr.Hdr, rr.Preference, rr.Host} } + func (rr *SIG) copy() RR { return &SIG{*rr.RRSIG.copy().(*RRSIG)} } + func (rr *SMIMEA) copy() RR { - return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} + return &SMIMEA{ + rr.Hdr, + rr.Usage, + rr.Selector, + rr.MatchingType, + rr.Certificate, + } } + func (rr *SOA) copy() RR { - return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl} + return &SOA{ + rr.Hdr, + rr.Ns, + rr.Mbox, + rr.Serial, + rr.Refresh, + rr.Retry, + rr.Expire, + rr.Minttl, + } } + func (rr *SPF) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &SPF{rr.Hdr, Txt} + return &SPF{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *SRV) copy() RR { - return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target} + return &SRV{ + rr.Hdr, + rr.Priority, + rr.Weight, + rr.Port, + rr.Target, + } } + func (rr *SSHFP) copy() RR { - return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint} + return &SSHFP{ + rr.Hdr, + rr.Algorithm, + rr.Type, + rr.FingerPrint, + } } + func (rr *SVCB) copy() RR { Value := make([]SVCBKeyValue, len(rr.Value)) for i, e := range rr.Value { Value[i] = e.copy() } - return &SVCB{rr.Hdr, rr.Priority, rr.Target, Value} + return &SVCB{ + rr.Hdr, + rr.Priority, + rr.Target, + Value, + } } + func (rr *TA) copy() RR { - return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} + return &TA{ + rr.Hdr, + rr.KeyTag, + rr.Algorithm, + rr.DigestType, + rr.Digest, + } } + func (rr *TALINK) copy() RR { return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName} } + func (rr *TKEY) copy() RR { - return &TKEY{rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData} + return &TKEY{ + rr.Hdr, + rr.Algorithm, + rr.Inception, + rr.Expiration, + rr.Mode, + rr.Error, + rr.KeySize, + rr.Key, + rr.OtherLen, + rr.OtherData, + } } + func (rr *TLSA) copy() RR { - return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} + return &TLSA{ + rr.Hdr, + rr.Usage, + rr.Selector, + rr.MatchingType, + rr.Certificate, + } } + func (rr *TSIG) copy() RR { - return &TSIG{rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData} + return &TSIG{ + rr.Hdr, + rr.Algorithm, + rr.TimeSigned, + rr.Fudge, + rr.MACSize, + rr.MAC, + rr.OrigId, + rr.Error, + rr.OtherLen, + rr.OtherData, + } } + func (rr *TXT) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &TXT{rr.Hdr, Txt} + return &TXT{rr.Hdr, cloneSlice(rr.Txt)} } + func (rr *UID) copy() RR { return &UID{rr.Hdr, rr.Uid} } + func (rr *UINFO) copy() RR { return &UINFO{rr.Hdr, rr.Uinfo} } + func (rr *URI) copy() RR { - return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target} + return &URI{ + rr.Hdr, + rr.Priority, + rr.Weight, + rr.Target, + } } + func (rr *X25) copy() RR { return &X25{rr.Hdr, rr.PSDNAddress} } + func (rr *ZONEMD) copy() RR { - return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest} + return &ZONEMD{ + rr.Hdr, + rr.Serial, + rr.Scheme, + rr.Hash, + rr.Digest, + } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 31aec320..311fed6a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -320,8 +320,8 @@ github.com/mattn/go-isatty # github.com/matttproud/golang_protobuf_extensions v1.0.4 ## explicit; go 1.9 github.com/matttproud/golang_protobuf_extensions/pbutil -# github.com/miekg/dns v1.1.50 -## explicit; go 1.14 +# github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 +## explicit; go 1.19 github.com/miekg/dns # github.com/mitchellh/copystructure v1.2.0 ## explicit; go 1.15 From e06b019baa7971f9460c2c4ddb729ec76c5f9442 Mon Sep 17 00:00:00 2001 From: ii2day Date: Thu, 23 Nov 2023 11:18:21 +0800 Subject: [PATCH 18/41] dns client udp connect read and write will not time out Signed-off-by: ii2day --- go.mod | 2 +- go.sum | 4 ++-- pkg/loadRequest/loadDns/dns_requester.go | 28 ++++++++++++++++------ pkg/loadRequest/loadHttp/http_requester.go | 3 +++ test/e2e/common/tools.go | 1 + test/e2e/netreach/netreach_test.go | 10 ++++---- vendor/github.com/miekg/dns/client.go | 2 +- vendor/modules.txt | 2 +- 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 7a6f08b0..a6f741bb 100644 --- a/go.mod +++ b/go.mod @@ -189,4 +189,4 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) -replace github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 +replace github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231123074630-7b2e2fc81556 diff --git a/go.sum b/go.sum index a97e34d6..53cff896 100644 --- a/go.sum +++ b/go.sum @@ -349,8 +349,8 @@ github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 h1:V github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 h1:AlmnjvSvhzwJR9YpV3r0k0b0g6de4infGN0fOImcaO4= -github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/kdoctor-io/dns v0.0.0-20231123074630-7b2e2fc81556 h1:5wa8cYpP1I3VMoX1WGOnDp2VmjEVgA481CIPTVNkBQE= +github.com/kdoctor-io/dns v0.0.0-20231123074630-7b2e2fc81556/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= 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.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index 5143e9b3..570ac02e 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -101,6 +101,7 @@ func (b *Work) Run() { go func() { c := time.After(time.Duration(b.RequestTimeSecond) * time.Second) ticker := time.NewTicker(time.Second) + defer ticker.Stop() // The request should be sent immediately at 0 seconds for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} @@ -199,6 +200,22 @@ func (b *Work) runWorker() { case <-b.stopCh: wg.Wait() if RequestProtocol(b.Protocol) == RequestMethodUdp { + // Wait for the last request to return + time.Sleep(time.Duration(b.Timeout) * time.Millisecond) + if len(conn.ResponseReceiver) > 0 { + for i := 0; i < len(conn.ResponseReceiver); i++ { + resp := <-conn.ResponseReceiver + e := resp.Err + if resp.Rtt > time.Duration(b.Timeout)*time.Millisecond { + e = fmt.Errorf("timeout for request, %d more than %d", resp.Rtt.Milliseconds(), b.Timeout) + } + b.results <- &result{ + duration: resp.Rtt, + err: e, + msg: resp.Msg, + } + } + } conn.ShutDownReceiver() conn.Close() } @@ -209,7 +226,6 @@ func (b *Work) runWorker() { *msg = *b.Msg msg.Id = dns.Id() go b.makeRequest(client, msg, conn, wg) - case resp := <-conn.ResponseReceiver: e := resp.Err if resp.Rtt > time.Duration(b.Timeout)*time.Millisecond { @@ -292,12 +308,10 @@ func (b *Work) makeConn(c *dns.Client) (*dns.Conn, error) { conn.ResponseReceiver = make(chan dns.Response, b.QPS) conn.ShutDown = make(chan struct{}) conn.Conn, err = d.Dial(b.Protocol, b.ServerAddr) - // write with the appropriate write timeout - t := time.Now() - writeDeadline := t.Add(time.Duration(b.RequestTimeSecond) * time.Second) - readDeadline := t.Add(time.Duration(b.RequestTimeSecond) * time.Second) - _ = conn.SetWriteDeadline(writeDeadline) - _ = conn.SetReadDeadline(readDeadline) + + // A zero value for t means Read and Write will not time out. + _ = conn.SetWriteDeadline(time.Time{}) + _ = conn.SetReadDeadline(time.Time{}) conn.TsigSecret, conn.TsigProvider = c.TsigSecret, c.TsigProvider return conn, err diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 2f5396a8..594cec2c 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -184,6 +184,7 @@ func (b *Work) Run() { go func() { c := time.After(time.Duration(b.RequestTimeSecond) * time.Second) ticker := time.NewTicker(time.Second) + defer ticker.Stop() // The request should be sent immediately at 0 seconds for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} @@ -192,6 +193,8 @@ func (b *Work) Run() { for { select { case <-c: + // Wait for the last request to return + time.Sleep(time.Duration(b.Timeout) * time.Millisecond) // Reach request duration stop request if len(b.qosTokenBucket) > 0 { b.Logger.Sugar().Errorf("request finish remaining number of tokens len: %d", len(b.qosTokenBucket)) diff --git a/test/e2e/common/tools.go b/test/e2e/common/tools.go index c476e2e6..7ab989a5 100644 --- a/test/e2e/common/tools.go +++ b/test/e2e/common/tools.go @@ -887,6 +887,7 @@ func GetRuntimeResource(f *frame.Framework, resource *v1beta1.TaskResource, ingr <-c switch resource.RuntimeType { + case kdoctor_types.KindDaemonSet: _, err := f.GetDaemonSet(resource.RuntimeName, TestNameSpace) if !errors.IsNotFound(err) { diff --git a/test/e2e/netreach/netreach_test.go b/test/e2e/netreach/netreach_test.go index 6671e13c..2413da97 100644 --- a/test/e2e/netreach/netreach_test.go +++ b/test/e2e/netreach/netreach_test.go @@ -14,12 +14,12 @@ import ( var _ = Describe("testing netReach ", Label("netReach"), func() { var termMin = int64(1) - // 1000ms is not stable on GitHub ci, so increased to 7000ms - var requestTimeout = 7000 + // 1000ms is not stable on GitHub ci, so increased to 9000ms + var requestTimeout = 9000 It("success testing netReach", Label("B00001", "C00004", "E00001"), func() { var e error successRate := float64(1) - successMean := int64(4000) + successMean := int64(4500) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -89,7 +89,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { It("Successfully testing using default daemonSet as workload with Task NetReach", Label("E00013"), func() { var e error successRate := float64(1) - successMean := int64(4000) + successMean := int64(4500) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -152,7 +152,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { It("Successfully testing using default daemonSet as workload with more Task NetReach", Label("E00016"), func() { var e error successRate := float64(1) - successMean := int64(4000) + successMean := int64(4500) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index b5f40e16..cb267140 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -58,8 +58,8 @@ func (co *Conn) tsigProvider() TsigProvider { } func (co *Conn) ShutDownReceiver() { - co.ShutDown <- struct{}{} co.Close() + co.ShutDown <- struct{}{} close(co.ResponseReceiver) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 311fed6a..d9232a6b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -320,7 +320,7 @@ github.com/mattn/go-isatty # github.com/matttproud/golang_protobuf_extensions v1.0.4 ## explicit; go 1.9 github.com/matttproud/golang_protobuf_extensions/pbutil -# github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231117104519-7085dea69b40 +# github.com/miekg/dns v1.1.50 => github.com/kdoctor-io/dns v0.0.0-20231123074630-7b2e2fc81556 ## explicit; go 1.19 github.com/miekg/dns # github.com/mitchellh/copystructure v1.2.0 From cc66949588d91e33c4f7ae9adfca33d8e3b57dc5 Mon Sep 17 00:00:00 2001 From: ii2day Date: Fri, 24 Nov 2023 10:33:11 +0800 Subject: [PATCH 19/41] adjust e2e netreach qps time Signed-off-by: ii2day --- test/e2e/netreach/netreach_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/netreach/netreach_test.go b/test/e2e/netreach/netreach_test.go index 2413da97..f4537713 100644 --- a/test/e2e/netreach/netreach_test.go +++ b/test/e2e/netreach/netreach_test.go @@ -123,8 +123,8 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 5 - request.DurationInSecond = 5 + request.QPS = 10 + request.DurationInSecond = 10 netReach.Spec.Request = request // Schedule @@ -186,8 +186,8 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 5 - request.DurationInSecond = 5 + request.QPS = 10 + request.DurationInSecond = 10 netReach.Spec.Request = request // Schedule From 5eed287f75717d13879ada30d5fbd0884dec98cc Mon Sep 17 00:00:00 2001 From: ii2day Date: Thu, 23 Nov 2023 11:38:20 +0800 Subject: [PATCH 20/41] doc Signed-off-by: ii2day --- docs/mkdocs.yml | 2 ++ docs/usage/apphttphealthy-zh_CN.md | 14 +++++++------- docs/usage/apphttphealthy.md | 14 +++++++------- docs/usage/debug-zh_CN.md | 3 +++ docs/usage/debug.md | 5 +++++ docs/usage/netdns-zh_CN.md | 14 +++++++------- docs/usage/netdns.md | 14 +++++++------- docs/usage/netreach-zh_CN.md | 14 +++++++------- docs/usage/netreach.md | 14 +++++++------- pkg/k8s/apis/system/v1beta1/common_types.go | 14 +++++++------- 10 files changed, 59 insertions(+), 49 deletions(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index bae5f6ad..db0b6870 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -50,6 +50,7 @@ nav: - AppHttpHealthy: usage/apphttphealthy.md - NetReach: usage/netreach.md - NetDns: usage/netdns.md + - Debug: usage/debug.md - Concepts: - Architecture: reference/arch.md - Runtime: concepts/runtime.md @@ -64,3 +65,4 @@ nav: - Development: - Release workflow: develop/release.md - Roadmap: develop/roadmap.md + - Contribution: develop/contributing.md diff --git a/docs/usage/apphttphealthy-zh_CN.md b/docs/usage/apphttphealthy-zh_CN.md index 4c0071c5..f1490a29 100644 --- a/docs/usage/apphttphealthy-zh_CN.md +++ b/docs/usage/apphttphealthy-zh_CN.md @@ -134,13 +134,13 @@ http true 1 1 succeed 0 1 EndTime: "2023-07-31T07:25:23Z" Errors: {} Latencies: - Max_inMx: 0 - Mean_inMs: 10.317307 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 10.317307 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 RequestCounts: 104 StartTime: "2023-07-31T07:25:12Z" StatusCodes: diff --git a/docs/usage/apphttphealthy.md b/docs/usage/apphttphealthy.md index eb582dc9..4cfb4591 100644 --- a/docs/usage/apphttphealthy.md +++ b/docs/usage/apphttphealthy.md @@ -134,13 +134,13 @@ http true 1 1 succeed 0 1 EndTime: "2023-07-31T07:25:23Z" Errors: {} Latencies: - Max_inMx: 0 - Mean_inMs: 10.317307 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 10.317307 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 RequestCounts: 104 StartTime: "2023-07-31T07:25:12Z" StatusCodes: diff --git a/docs/usage/debug-zh_CN.md b/docs/usage/debug-zh_CN.md index 086b6f23..bcd04244 100644 --- a/docs/usage/debug-zh_CN.md +++ b/docs/usage/debug-zh_CN.md @@ -35,3 +35,6 @@ NetReachQPS: 0 ... ``` +**Q: 为什么 Kdoctor agent 会 OOM?** +* A: kdoctor agent 作为默认的执行任务的 agent,在任务中没有指定 agent 时,默认使用 kdoctor agent 执行,目前的 agent 还不支持根据任务负载情况,拒绝执行任务或延迟执行任务功能。 + 因此当 kdoctor agent 同时执行大量任务时,由于请求量过大,内存限制过低,将会导致 kdoctor agent 内存过载,导致 OOM,我们可以根据任务情况,错开使用 kdoctor agent,调整内存限制,或使用指定的 agent 隔离任务。 diff --git a/docs/usage/debug.md b/docs/usage/debug.md index 4e1f89de..965a3f5b 100644 --- a/docs/usage/debug.md +++ b/docs/usage/debug.md @@ -39,3 +39,8 @@ NetReachQPS: 0 ... ``` +**Q: Why does the Kdoctor agent experience OOM (Out of Memory) issues ?** +* A: The Kdoctor agent serves as the default agent for executing tasks when no specific agent is specified in the task. Currently, the agent does not support features to reject or delay task execution based on the workload. + Therefore, when the Kdoctor agent concurrently handles a large number of tasks, the high request volume combined with low memory limits can lead to memory overload and result in OOM (Out of Memory) errors. + To mitigate this issue, you can stagger the usage of the Kdoctor agent based on the task workload, adjust the memory limits accordingly, or isolate tasks using specific agents that are better suited for the workload. + diff --git a/docs/usage/netdns-zh_CN.md b/docs/usage/netdns-zh_CN.md index d7d4bd6a..a40b41de 100644 --- a/docs/usage/netdns-zh_CN.md +++ b/docs/usage/netdns-zh_CN.md @@ -148,13 +148,13 @@ netdns-cluster true 1 1 succeed 0 1 Errors: {} FailedCounts: 0 Latencies: - Max_inMx: 0 - Mean_inMs: 0.2970297 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 0.2970297 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 ReplyCode: NOERROR: 101 RequestCounts: 101 diff --git a/docs/usage/netdns.md b/docs/usage/netdns.md index 06285193..7e07fd92 100644 --- a/docs/usage/netdns.md +++ b/docs/usage/netdns.md @@ -150,13 +150,13 @@ netdns-cluster true 1 1 succeed 0 1 Errors: {} FailedCounts: 0 Latencies: - Max_inMx: 0 - Mean_inMs: 0.2970297 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 0.2970297 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 ReplyCode: NOERROR: 101 RequestCounts: 101 diff --git a/docs/usage/netreach-zh_CN.md b/docs/usage/netreach-zh_CN.md index 916519a9..dfe8e1f4 100644 --- a/docs/usage/netreach-zh_CN.md +++ b/docs/usage/netreach-zh_CN.md @@ -110,13 +110,13 @@ task true 1 1 succeed 0 1 EndTime: "2023-08-01T08:37:06Z" Errors: {} Latencies: - Max_inMx: 0 - Mean_inMs: 23.08 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 23.08 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 RequestCounts: 100 StartTime: "2023-08-01T08:36:56Z" StatusCodes: diff --git a/docs/usage/netreach.md b/docs/usage/netreach.md index 8b22de5b..1bb961ef 100644 --- a/docs/usage/netreach.md +++ b/docs/usage/netreach.md @@ -110,13 +110,13 @@ task true 1 1 succeed 0 1 EndTime: "2023-08-01T08:37:06Z" Errors: {} Latencies: - Max_inMx: 0 - Mean_inMs: 23.08 - Min_inMs: 0 - P50_inMs: 0 - P90_inMs: 0 - P95_inMs: 0 - P99_inMs: 0 + MaxInMs: 0 + MeanInMs: 23.08 + MinInMs: 0 + P50InMs: 0 + P90InMs: 0 + P95InMs: 0 + P99InMs: 0 RequestCounts: 100 StartTime: "2023-08-01T08:36:56Z" StatusCodes: diff --git a/pkg/k8s/apis/system/v1beta1/common_types.go b/pkg/k8s/apis/system/v1beta1/common_types.go index c2635d51..879fa197 100644 --- a/pkg/k8s/apis/system/v1beta1/common_types.go +++ b/pkg/k8s/apis/system/v1beta1/common_types.go @@ -5,19 +5,19 @@ package v1beta1 type LatencyDistribution struct { // P50 is the 50th percentile request latency. - P50 float32 `json:"P50_inMs"` + P50 float32 `json:"P50InMs"` // P90 is the 90th percentile request latency. - P90 float32 `json:"P90_inMs"` + P90 float32 `json:"P90InMs"` // P95 is the 95th percentile request latency. - P95 float32 `json:"P95_inMs"` + P95 float32 `json:"P95InMs"` // P99 is the 99th percentile request latency. - P99 float32 `json:"P99_inMs"` + P99 float32 `json:"P99InMs"` // Max is the maximum observed request latency. - Max float32 `json:"Max_inMx"` + Max float32 `json:"MaxInMs"` // Min is the minimum observed request latency. - Min float32 `json:"Min_inMs"` + Min float32 `json:"MinInMs"` // Mean is the mean request latency. - Mean float32 `json:"Mean_inMs"` + Mean float32 `json:"MeanInMs"` } type TotalRunningLoad struct { From c8dc481c04eaaf1e98f0375e5eb5eb83f24a362e Mon Sep 17 00:00:00 2001 From: ii2day Date: Mon, 27 Nov 2023 11:42:45 +0800 Subject: [PATCH 21/41] Add request token sending counter Signed-off-by: ii2day --- pkg/loadRequest/loadDns/dns_requester.go | 11 +++++++++++ pkg/loadRequest/loadHttp/http_requester.go | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index 570ac02e..aa067f01 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -99,6 +99,9 @@ func (b *Work) Run() { // Send qps number of tokens to the channel qosTokenBucket every second to the coroutine for execution go func() { + // Request token counter to avoid issuing multiple tokens due to errors + requestRound := 0 + c := time.After(time.Duration(b.RequestTimeSecond) * time.Second) ticker := time.NewTicker(time.Second) defer ticker.Stop() @@ -106,10 +109,13 @@ func (b *Work) Run() { for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} } + requestRound++ + b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for { select { case <-c: + b.Logger.Sugar().Debugf("reach request duration time, stop request") // Reach request duration stop request if len(b.qosTokenBucket) > 0 { b.Logger.Sugar().Errorf("request finish remaining number of tokens len: %d", len(b.qosTokenBucket)) @@ -118,10 +124,15 @@ func (b *Work) Run() { b.Stop() return case <-ticker.C: + if requestRound >= b.RequestTimeSecond { + b.Logger.Sugar().Debugf("All request tokens have been sent and will not be sent again.") + continue + } b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} } + requestRound++ } } }() diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 594cec2c..1992e86f 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -182,17 +182,24 @@ func (b *Work) Run() { // Send qps number of tokens to the channel qosTokenBucket every second to the coroutine for execution go func() { + // Request token counter to avoid issuing multiple tokens due to errors + requestRound := 0 + c := time.After(time.Duration(b.RequestTimeSecond) * time.Second) ticker := time.NewTicker(time.Second) defer ticker.Stop() + // The request should be sent immediately at 0 seconds for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} } + requestRound++ + b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for { select { case <-c: + b.Logger.Sugar().Debugf("reach request duration time, stop request") // Wait for the last request to return time.Sleep(time.Duration(b.Timeout) * time.Millisecond) // Reach request duration stop request @@ -203,10 +210,15 @@ func (b *Work) Run() { b.Stop() return case <-ticker.C: + if requestRound >= b.RequestTimeSecond { + b.Logger.Sugar().Debugf("All request tokens have been sent and will not be sent again.") + continue + } b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for i := 0; i < b.QPS; i++ { b.qosTokenBucket <- struct{}{} } + requestRound++ } } }() From 3e06e1f12b3a676cab2819bb8f05a12116c477da Mon Sep 17 00:00:00 2001 From: ii2day Date: Mon, 27 Nov 2023 17:47:44 +0800 Subject: [PATCH 22/41] e2e Signed-off-by: ii2day --- pkg/loadRequest/loadDns/dns_requester.go | 4 +- pkg/loadRequest/loadHttp/http_requester.go | 2 - test/docs/NetDns.md | 11 +- test/docs/Schedule.md | 6 +- test/e2e/apphttphealth/apphttphealth_test.go | 155 ++++--------------- test/e2e/netdns/netdns_test.go | 125 +++++++-------- test/e2e/netreach/netreach_test.go | 41 +++-- 7 files changed, 118 insertions(+), 226 deletions(-) diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index aa067f01..22951569 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -210,9 +210,9 @@ func (b *Work) runWorker() { select { case <-b.stopCh: wg.Wait() + // Wait for the last request to return + time.Sleep(time.Duration(b.Timeout) * time.Millisecond) if RequestProtocol(b.Protocol) == RequestMethodUdp { - // Wait for the last request to return - time.Sleep(time.Duration(b.Timeout) * time.Millisecond) if len(conn.ResponseReceiver) > 0 { for i := 0; i < len(conn.ResponseReceiver); i++ { resp := <-conn.ResponseReceiver diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 1992e86f..15db93b0 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -200,8 +200,6 @@ func (b *Work) Run() { select { case <-c: b.Logger.Sugar().Debugf("reach request duration time, stop request") - // Wait for the last request to return - time.Sleep(time.Duration(b.Timeout) * time.Millisecond) // Reach request duration stop request if len(b.qosTokenBucket) > 0 { b.Logger.Sugar().Errorf("request finish remaining number of tokens len: %d", len(b.qosTokenBucket)) diff --git a/test/docs/NetDns.md b/test/docs/NetDns.md index cb4daa49..cf3e88f6 100644 --- a/test/docs/NetDns.md +++ b/test/docs/NetDns.md @@ -1,7 +1,8 @@ # E2E Cases for NetDns -| Case ID | Title | Priority | Smoke | Status | Other | -|---------|-------------------------------------------------------------------|----------|-------|--------|-------------| -| D00001 | Successfully testing Cluster Dns Server case | p1 | | done | | -| D00002 | Successfully testing User Define Dns server case | p1 | | done | | -| D00003 | Successfully testing User Define Dns server case use tcp protocol | p1 | | done | | +| Case ID | Title | Priority | Smoke | Status | Other | +|---------|-----------------------------------------------------------------------|----------|-------|--------|-------------| +| D00001 | Successfully testing Cluster Dns Server case | p1 | | done | | +| D00002 | Successfully testing User Define Dns server case | p1 | | done | | +| D00003 | Successfully testing User Define Dns server case use tcp protocol | p1 | | done | | +| D00004 | Successfully testing User Define Dns server case use tcp-tls protocol | p1 | | | | diff --git a/test/docs/Schedule.md b/test/docs/Schedule.md index cd1fa74e..f0c8ca1d 100644 --- a/test/docs/Schedule.md +++ b/test/docs/Schedule.md @@ -2,9 +2,9 @@ | Case ID | Title | Priority | Smoke | Status | Other | |---------|-------------------------------------------------|----------|-------|--------|-------------| -| C00001 | Successfully testing NetReach crontab case | p1 | | | | -| C00002 | Successfully testing NetDns crontab case | p1 | | | | -| C00003 | Successfully testing AppHttpHealth crontab case | p1 | | | | +| C00001 | Successfully testing NetReach crontab case | p1 | | done | | +| C00002 | Successfully testing NetDns crontab case | p1 | | done | | +| C00003 | Successfully testing AppHttpHealth crontab case | p1 | | done | | | C00004 | Successfully testing NetReach sample case | p1 | | done | | | C00005 | Successfully testing NetDns sample case | p1 | | done | | | C00006 | Successfully testing AppHttpHealth sample case | p1 | | done | | diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index d499f695..7eef0935 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -7,36 +7,32 @@ import ( "fmt" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" "github.com/kdoctor-io/kdoctor/pkg/pluginManager" + "github.com/kdoctor-io/kdoctor/pkg/types" "github.com/kdoctor-io/kdoctor/test/e2e/common" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/spidernet-io/e2eframework/tools" + "k8s.io/utils/pointer" "net" ) -var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { - var termMin = int64(1) - // 2000ms is not stable on GitHub ci, so increased to 3000ms +var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), Serial, func() { + // 2000ms is not stable on GitHub ci, so increased to 7000ms // issue : https://github.com/kdoctor-io/kdoctor/issues/222 // issue : https://github.com/kdoctor-io/kdoctor/issues/223 // issue : https://github.com/kdoctor-io/kdoctor/issues/165 // issue : https://github.com/kdoctor-io/kdoctor/issues/96 - var requestTimeout = 3000 - It("success http testing appHttpHealth method GET", Label("A00001", "A00011", "C00006", "E00002", "A00014"), func() { + var requestTimeout = 7000 + var successMean = int64(3000) + It("success http testing appHttpHealth method GET", Serial, Label("A00001", "A00011", "C00006", "E00002", "A00014", "E00017"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agent - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -81,14 +77,11 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("failed http testing appHttpHealth due to status code", Label("A00002"), func() { + It("failed http testing appHttpHealth due to status code", Serial, Label("A00002"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" expectStatusCode := 205 appHttpHealthName := "apphttphealth-get" + tools.RandomName() @@ -96,11 +89,6 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -146,11 +134,9 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeFalse(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Failed http testing appHttpHealth due to delay ", Label("A00003"), func() { + It("Failed http testing appHttpHealth due to delay ", Serial, Label("A00003"), func() { var e error successRate := float64(1) successMean := int64(200) @@ -160,11 +146,6 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -209,24 +190,16 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeFalse(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("success https testing appHttpHealth method GET", Label("A00004"), func() { + It("success https testing appHttpHealth method GET", Serial, Label("A00004"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -273,25 +246,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("failed https testing appHttpHealth due to tls", Label("A00005"), func() { + It("failed https testing appHttpHealth due to tls", Serial, Label("A00005"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -338,26 +303,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, []string{}, reportNum, appHttpHealth) Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeFalse(), "compare report and task result") - - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully http testing appHttpHealth method PUT ", Label("A00006"), func() { + It("Successfully http testing appHttpHealth method PUT ", Serial, Label("A00006"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-put" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -402,26 +358,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") - }) - It("Successfully http testing appHttpHealth method POST With Body", Label("A00007"), func() { + It("Successfully http testing appHttpHealth method POST With Body", Serial, Label("A00007"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-post" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -469,25 +416,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully http testing appHttpHealth method HEAD", Label("A00008"), func() { + It("Successfully http testing appHttpHealth method HEAD", Serial, Label("A00008"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-head" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -532,25 +471,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully http testing appHttpHealth method PATCH", Label("A00009"), func() { + It("Successfully http testing appHttpHealth method PATCH", Serial, Label("A00009"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-patch" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -595,25 +526,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully http testing appHttpHealth method OPTIONS", Label("A00010"), func() { + It("Successfully http testing appHttpHealth method OPTIONS", Serial, Label("A00010"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-options" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -658,25 +581,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully http testing appHttpHealth due to success rate", Label("A00012"), func() { + It("Successfully http testing appHttpHealth due to success rate", Serial, Label("A00012"), func() { var e error successRate := float64(0.2) - successMean := int64(1200) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -721,25 +636,17 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully https testing appHttpHealth method GET Protocol Http2", Label("A00013"), func() { + It("Successfully https testing appHttpHealth method GET Protocol Http2", Serial, Label("A00013"), func() { var e error successRate := float64(1) - successMean := int64(3000) crontab := "0 1" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -787,15 +694,12 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully testing using default daemonSet as workload with Task AppHttpHealthy ", Label("E00014"), func() { + It("Successfully testing AppHttpHealth crontab case", Serial, Label("C00003"), func() { var e error successRate := float64(1) - successMean := int64(3000) - crontab := "0 1" + crontab := "* * * * *" appHttpHealthName := "apphttphealth-get" + tools.RandomName() appHttpHealth := new(v1beta1.AppHttpHealthy) @@ -821,8 +725,8 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 5 - request.DurationInSecond = 5 + request.QPS = 10 + request.DurationInSecond = 10 appHttpHealth.Spec.Request = request // Schedule @@ -847,7 +751,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { }) - It("Successfully testing using default daemonSet as workload with more Task AppHttpHealthy", Label("E00017"), func() { + It("Successfully testing Task NetAppHttpHealthy Runtime Deployment Service creation", Serial, Label("E00005"), func() { var e error successRate := float64(1) successMean := int64(3000) @@ -857,6 +761,13 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { appHttpHealth := new(v1beta1.AppHttpHealthy) appHttpHealth.Name = appHttpHealthName + // agent + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + agentSpec.Kind = types.KindDeployment + agentSpec.DeploymentReplicas = pointer.Int32(2) + appHttpHealth.Spec.AgentSpec = agentSpec + // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -877,8 +788,8 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 5 - request.DurationInSecond = 5 + request.QPS = 10 + request.DurationInSecond = 10 appHttpHealth.Spec.Request = request // Schedule @@ -901,5 +812,7 @@ var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") + e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) }) diff --git a/test/e2e/netdns/netdns_test.go b/test/e2e/netdns/netdns_test.go index 07376e48..c8762bb8 100644 --- a/test/e2e/netdns/netdns_test.go +++ b/test/e2e/netdns/netdns_test.go @@ -7,20 +7,22 @@ import ( "fmt" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" "github.com/kdoctor-io/kdoctor/pkg/pluginManager" + "github.com/kdoctor-io/kdoctor/pkg/types" "github.com/kdoctor-io/kdoctor/test/e2e/common" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/spidernet-io/e2eframework/tools" + "k8s.io/utils/pointer" ) var _ = Describe("testing netDns ", Label("netDns"), func() { var targetDomain = "%s.kubernetes.default.svc.cluster.local" - var termMin = int64(1) - It("Successfully testing Cluster Dns Server case", Label("D00001", "C00005", "E00003"), func() { + successMean := int64(1500) + successRate := float64(1) + It("Successfully testing Cluster Dns Server case", Label("D00001", "C00005", "E00003", "E00015", "E00018"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(1500) + crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() @@ -28,11 +30,6 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { netDns := new(v1beta1.Netdns) netDns.Name = netDnsName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - netDns.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -52,10 +49,10 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 2000 request.QPS = 10 request.DurationInSecond = 10 - request.Domain = fmt.Sprintf(targetDomain, netDnsName) + request.Domain = "kubernetes.default.svc.cluster.local" protocol := "udp" request.Protocol = &protocol netDns.Spec.Request = request @@ -80,26 +77,16 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).NotTo(BeFalse(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, netDnsName, pluginManager.KindNameNetdns, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") - }) - It("Successfully testing User Define Dns server case", Label("D00002"), func() { + It("Successfully testing User Define Dns server case", Label("D00002"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(1500) crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() netDns := new(v1beta1.Netdns) netDns.Name = netDnsName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - netDns.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -118,7 +105,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1000 + request.PerRequestTimeoutInMS = 2000 request.QPS = 10 request.DurationInSecond = 10 request.Domain = fmt.Sprintf(targetDomain, netDnsName) @@ -146,17 +133,13 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - e = common.CheckRuntimeDeadLine(frame, netDnsName, pluginManager.KindNameNetdns, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully testing using default daemonSet as workload with Task NetDns ", Label("E00015"), func() { + It("Successfully testing User Define Dns server case use tcp protocol", Serial, Label("D00003"), func() { var e error - successRate := float64(1) - successMean := int64(1500) crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() - // netdns + netDns := new(v1beta1.Netdns) netDns.Name = netDnsName @@ -168,22 +151,21 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // target target := new(v1beta1.NetDnsTarget) - targetDns := new(v1beta1.NetDnsTargetDnsSpec) - targetDns.TestIPv4 = &common.TestIPv4 - targetDns.TestIPv6 = &common.TestIPv6 - targetDns.ServiceName = &KubeDnsName - targetDns.ServiceNamespace = &KubeDnsNamespace - target.NetDnsTargetDns = targetDns + targetDnsUser := new(v1beta1.NetDnsTargetUserSpec) + targetDnsUser.Server = &testSvcIP + port := 53 + targetDnsUser.Port = &port + target.NetDnsTargetUser = targetDnsUser target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1500 - request.QPS = 5 - request.DurationInSecond = 5 - request.Domain = "kubernetes.default.svc.cluster.local" - protocol := "udp" + request.PerRequestTimeoutInMS = 2000 + request.QPS = 10 + request.DurationInSecond = 10 + request.Domain = fmt.Sprintf(targetDomain, netDnsName) + protocol := "tcp" request.Protocol = &protocol netDns.Spec.Request = request @@ -203,19 +185,16 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { e = common.WaitKdoctorTaskDone(frame, netDns, pluginManager.KindNameNetdns, 120) Expect(e).NotTo(HaveOccurred(), "wait netDns task finish") - success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, []string{}, reportNum, netDns) + success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, testPodIPs, reportNum, netDns) Expect(e).NotTo(HaveOccurred(), "compare report and task") - Expect(success).NotTo(BeFalse(), "compare report and task result") - + Expect(success).To(BeTrue(), "compare report and task result") }) - It("Successfully testing using default daemonSet as workload with more Task NetDns ", Label("E00018"), func() { + It("Successfully testing NetDns crontab case", Label("C00002"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(1500) - crontab := "0 1" + crontab := "* * * * *" netDnsName := "netdns-e2e-" + tools.RandomName() - // netdns + netDns := new(v1beta1.Netdns) netDns.Name = netDnsName @@ -227,21 +206,20 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // target target := new(v1beta1.NetDnsTarget) - targetDns := new(v1beta1.NetDnsTargetDnsSpec) - targetDns.TestIPv4 = &common.TestIPv4 - targetDns.TestIPv6 = &common.TestIPv6 - targetDns.ServiceName = &KubeDnsName - targetDns.ServiceNamespace = &KubeDnsNamespace - target.NetDnsTargetDns = targetDns + targetDnsUser := new(v1beta1.NetDnsTargetUserSpec) + targetDnsUser.Server = &testSvcIP + port := 53 + targetDnsUser.Port = &port + target.NetDnsTargetUser = targetDnsUser target.EnableLatencyMetric = true netDns.Spec.Target = target // request request := new(v1beta1.NetdnsRequest) - request.PerRequestTimeoutInMS = 1500 - request.QPS = 5 - request.DurationInSecond = 5 - request.Domain = "kubernetes.default.svc.cluster.local" + request.PerRequestTimeoutInMS = 2000 + request.QPS = 10 + request.DurationInSecond = 10 + request.Domain = fmt.Sprintf(targetDomain, netDnsName) protocol := "udp" request.Protocol = &protocol netDns.Spec.Request = request @@ -262,25 +240,28 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { e = common.WaitKdoctorTaskDone(frame, netDns, pluginManager.KindNameNetdns, 120) Expect(e).NotTo(HaveOccurred(), "wait netDns task finish") - success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, []string{}, reportNum, netDns) + success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, testPodIPs, reportNum, netDns) Expect(e).NotTo(HaveOccurred(), "compare report and task") - Expect(success).NotTo(BeFalse(), "compare report and task result") + Expect(success).To(BeTrue(), "compare report and task result") }) - It("Successfully testing User Define Dns server case use tcp protocol", Label("D00003"), func() { + It("Successfully testing Task NetDns Runtime Deployment Service creation", Serial, Label("E00006"), func() { var e error successRate := float64(1) successMean := int64(1500) crontab := "0 1" netDnsName := "netdns-e2e-" + tools.RandomName() + // netdns netDns := new(v1beta1.Netdns) netDns.Name = netDnsName // agentSpec agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + agentSpec.Kind = types.KindDeployment + agentSpec.DeploymentReplicas = pointer.Int32(2) netDns.Spec.AgentSpec = agentSpec // successCondition @@ -291,11 +272,12 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { // target target := new(v1beta1.NetDnsTarget) - targetDnsUser := new(v1beta1.NetDnsTargetUserSpec) - targetDnsUser.Server = &testSvcIP - port := 53 - targetDnsUser.Port = &port - target.NetDnsTargetUser = targetDnsUser + targetDns := new(v1beta1.NetDnsTargetDnsSpec) + targetDns.TestIPv4 = &common.TestIPv4 + targetDns.TestIPv6 = &common.TestIPv6 + targetDns.ServiceName = &KubeDnsName + targetDns.ServiceNamespace = &KubeDnsNamespace + target.NetDnsTargetDns = targetDns target.EnableLatencyMetric = true netDns.Spec.Target = target @@ -304,8 +286,8 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { request.PerRequestTimeoutInMS = 1000 request.QPS = 10 request.DurationInSecond = 10 - request.Domain = fmt.Sprintf(targetDomain, netDnsName) - protocol := "tcp" + request.Domain = "kubernetes.default.svc.cluster.local" + protocol := "udp" request.Protocol = &protocol netDns.Spec.Request = request @@ -325,11 +307,12 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { e = common.WaitKdoctorTaskDone(frame, netDns, pluginManager.KindNameNetdns, 120) Expect(e).NotTo(HaveOccurred(), "wait netDns task finish") - success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, testPodIPs, reportNum, netDns) + success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, []string{}, reportNum, netDns) Expect(e).NotTo(HaveOccurred(), "compare report and task") - Expect(success).To(BeTrue(), "compare report and task result") + Expect(success).NotTo(BeFalse(), "compare report and task result") e = common.CheckRuntimeDeadLine(frame, netDnsName, pluginManager.KindNameNetdns, 120) Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") + }) }) diff --git a/test/e2e/netreach/netreach_test.go b/test/e2e/netreach/netreach_test.go index f4537713..a97a97c7 100644 --- a/test/e2e/netreach/netreach_test.go +++ b/test/e2e/netreach/netreach_test.go @@ -6,31 +6,27 @@ package netreach_test import ( "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/kdoctor.io/v1beta1" "github.com/kdoctor-io/kdoctor/pkg/pluginManager" + "github.com/kdoctor-io/kdoctor/pkg/types" "github.com/kdoctor-io/kdoctor/test/e2e/common" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/spidernet-io/e2eframework/tools" + "k8s.io/utils/pointer" ) var _ = Describe("testing netReach ", Label("netReach"), func() { - var termMin = int64(1) // 1000ms is not stable on GitHub ci, so increased to 9000ms - var requestTimeout = 9000 - It("success testing netReach", Label("B00001", "C00004", "E00001"), func() { + var requestTimeout = 15000 + var successRate = float64(1) + var successMean = int64(7000) + It("success testing netReach", Label("B00001", "C00004", "E00001", "E00013", "E00016"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(4500) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() netReach := new(v1beta1.NetReach) netReach.Name = netReachName - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - netReach.Spec.AgentSpec = agentSpec - // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -81,16 +77,11 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { success, e := common.CompareResult(frame, netReachName, pluginManager.KindNameNetReach, []string{}, reportNum, netReach) Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - - e = common.CheckRuntimeDeadLine(frame, netReachName, pluginManager.KindNameNetReach, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - It("Successfully testing using default daemonSet as workload with Task NetReach", Label("E00013"), func() { + It("Successfully testing NetReach crontab case", Label("C00001"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(4500) - crontab := "0 1" + crontab := "* * * * *" netReachName := "netreach-" + tools.RandomName() netReach := new(v1beta1.NetReach) @@ -117,8 +108,8 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { target.MultusInterface = &disable target.IPv4 = &common.TestIPv4 target.IPv6 = &common.TestIPv6 - netReach.Spec.Target = target target.EnableLatencyMetric = true + netReach.Spec.Target = target // request request := new(v1beta1.NetHttpRequest) @@ -146,19 +137,23 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { success, e := common.CompareResult(frame, netReachName, pluginManager.KindNameNetReach, []string{}, reportNum, netReach) Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") - }) - It("Successfully testing using default daemonSet as workload with more Task NetReach", Label("E00016"), func() { + It("Successfully testing Task NetReach Runtime Deployment Service Ingress creation ", Label("E00004"), Serial, func() { var e error - successRate := float64(1) - successMean := int64(4500) crontab := "0 1" netReachName := "netreach-" + tools.RandomName() netReach := new(v1beta1.NetReach) netReach.Name = netReachName + // agentSpec + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + agentSpec.Kind = types.KindDeployment + agentSpec.DeploymentReplicas = pointer.Int32(2) + netReach.Spec.AgentSpec = agentSpec + // successCondition successCondition := new(v1beta1.NetSuccessCondition) successCondition.SuccessRate = &successRate @@ -210,5 +205,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") + e = common.CheckRuntimeDeadLine(frame, netReachName, pluginManager.KindNameNetReach, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) }) From 1b92983308b437407b229fbc424674718c2a9457 Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 28 Nov 2023 14:25:13 +0800 Subject: [PATCH 23/41] format service ipv6 ip Signed-off-by: ii2day --- pkg/k8ObjManager/service.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pkg/k8ObjManager/service.go b/pkg/k8ObjManager/service.go index 0bcf18b8..c7abfb94 100644 --- a/pkg/k8ObjManager/service.go +++ b/pkg/k8ObjManager/service.go @@ -10,6 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "net" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -73,11 +74,21 @@ func (nm *k8sObjManager) GetServiceAccessUrl(ctx context.Context, name, namespac // get clusterip url if len(s.Spec.ClusterIPs) > 0 { for _, v := range s.Spec.ClusterIPs { - t := fmt.Sprintf("%s:%v", v, s.Spec.Ports[portIndex].Port) + var t string + if net.ParseIP(v).To4() == nil { + t = fmt.Sprintf("[%s]:%v", v, s.Spec.Ports[portIndex].Port) + } else { + t = fmt.Sprintf("%s:%v", v, s.Spec.Ports[portIndex].Port) + } r.ClusterIPUrl = append(r.ClusterIPUrl, t) } } else if len(s.Spec.ClusterIP) > 0 { - t := fmt.Sprintf("%s:%v", s.Spec.ClusterIP, s.Spec.Ports[portIndex].Port) + var t string + if net.ParseIP(s.Spec.ClusterIP).To4() == nil { + t = fmt.Sprintf("[%s]:%v", s.Spec.ClusterIP, s.Spec.Ports[portIndex].Port) + } else { + t = fmt.Sprintf("%s:%v", s.Spec.ClusterIP, s.Spec.Ports[portIndex].Port) + } r.ClusterIPUrl = append(r.ClusterIPUrl, t) } @@ -87,7 +98,12 @@ func (nm *k8sObjManager) GetServiceAccessUrl(ctx context.Context, name, namespac // get loadbalancer url if len(s.Status.LoadBalancer.Ingress) > 0 { for _, v := range s.Status.LoadBalancer.Ingress { - t := fmt.Sprintf("%s:%v", v.IP, s.Spec.Ports[portIndex].Port) + var t string + if net.ParseIP(s.Spec.ClusterIP).To4() == nil { + t = fmt.Sprintf("[%s]:%v", v.IP, s.Spec.Ports[portIndex].Port) + } else { + t = fmt.Sprintf("%s:%v", v.IP, s.Spec.Ports[portIndex].Port) + } r.LoadBalancerUrl = append(r.LoadBalancerUrl, t) } } From 733a41891dd3abc1a78e4173ffd1fa28be49155a Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 28 Nov 2023 17:15:14 +0800 Subject: [PATCH 24/41] add http trace log Signed-off-by: ii2day --- pkg/loadRequest/loadHttp/http_requester.go | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 15db93b0..7907d088 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -53,10 +53,6 @@ const MaxResultChannelSize = 1000000 type result struct { err error duration time.Duration - connDuration time.Duration // connection setup(DNS lookup + Dial up) duration - dnsDuration time.Duration // dns lookup duration - reqDuration time.Duration // request "write" duration - resDuration time.Duration // response "read" duration statusCode int contentLength int64 } @@ -244,8 +240,8 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { defer wg.Done() s := b.now() var size int64 - var dnsStart, connStart, resStart time.Duration - var dnsDuration, connDuration, resDuration, reqDuration time.Duration + var dnsStart, connStart, resStart, reqStart, delayStart time.Duration + var dnsDuration, connDuration, resDuration, reqDuration, delayDuration time.Duration req := genRequest(b.Request, b.RequestBody) trace := &httptrace.ClientTrace{ DNSStart: func(info httptrace.DNSStartInfo) { @@ -261,6 +257,15 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { if !connInfo.Reused { connDuration = b.now() - connStart } + reqStart = b.now() + }, + WroteRequest: func(w httptrace.WroteRequestInfo) { + reqDuration = b.now() - reqStart + delayStart = b.now() + }, + GotFirstResponseByte: func() { + delayDuration = b.now() - delayStart + resStart = b.now() }, } req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) @@ -278,6 +283,12 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { statusCode = resp.StatusCode } else { statusCode = 0 + b.Logger.Sugar().Debugf("request err: %v", err) + b.Logger.Sugar().Debugf("connection setup(DNS lookup + Dial up) duration: %d", connDuration.Milliseconds()) + b.Logger.Sugar().Debugf("dns lookup duration: %d", dnsDuration.Milliseconds()) + b.Logger.Sugar().Debugf("request write duration: %d", reqDuration.Milliseconds()) + b.Logger.Sugar().Debugf("response read duration: %d", resDuration.Milliseconds()) + b.Logger.Sugar().Debugf("delay between response and request: %d", delayDuration.Milliseconds()) } if b.ExpectStatusCode != nil { if statusCode != *b.ExpectStatusCode { @@ -292,10 +303,6 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { statusCode: statusCode, err: err, contentLength: size, - connDuration: connDuration, - dnsDuration: dnsDuration, - reqDuration: reqDuration, - resDuration: resDuration, } } From a3a899d0839552f2f1ea05a30f65458d85c116e3 Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 28 Nov 2023 19:43:12 +0800 Subject: [PATCH 25/41] add took lock time threshold to 0.5 Signed-off-by: ii2day --- pkg/lock/lock_debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lock/lock_debug.go b/pkg/lock/lock_debug.go index b0c72395..9f2f593b 100644 --- a/pkg/lock/lock_debug.go +++ b/pkg/lock/lock_debug.go @@ -19,7 +19,7 @@ import ( const ( // selfishThresholdSec is the number of seconds that should be used when // detecting if a lock was held for more than the specified time. - selfishThresholdSec = 0.1 + selfishThresholdSec = 0.5 // Waiting for a lock for longer than DeadlockTimeout is considered a deadlock. // Ignored is DeadlockTimeout <= 0. From 0886197a68d336ae82718c7a9f24f80e8a8f4dbf Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 28 Nov 2023 14:16:44 +0800 Subject: [PATCH 26/41] fix took long lock Signed-off-by: ii2day --- pkg/loadRequest/loadDns/dns_requester.go | 2 ++ pkg/loadRequest/loadHttp/http_requester.go | 2 ++ pkg/pluginManager/netdns/agentExecuteTask.go | 2 +- pkg/pluginManager/netreach/agentExecuteTask.go | 4 ++-- test/Makefile | 6 ++++-- test/e2e/apphttphealth/apphttphealth_test.go | 6 +++--- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/pkg/loadRequest/loadDns/dns_requester.go b/pkg/loadRequest/loadDns/dns_requester.go index 22951569..a012ae1e 100644 --- a/pkg/loadRequest/loadDns/dns_requester.go +++ b/pkg/loadRequest/loadDns/dns_requester.go @@ -110,6 +110,7 @@ func (b *Work) Run() { b.qosTokenBucket <- struct{}{} } requestRound++ + b.Logger.Sugar().Debugf("send token %d times", requestRound) b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for { @@ -133,6 +134,7 @@ func (b *Work) Run() { b.qosTokenBucket <- struct{}{} } requestRound++ + b.Logger.Sugar().Debugf("send token %d times", requestRound) } } }() diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index 15db93b0..2e8fdb7f 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -194,6 +194,7 @@ func (b *Work) Run() { b.qosTokenBucket <- struct{}{} } requestRound++ + b.Logger.Sugar().Debugf("send token %d times", requestRound) b.Logger.Sugar().Debugf("request token channel len: %d", len(b.qosTokenBucket)) for { @@ -217,6 +218,7 @@ func (b *Work) Run() { b.qosTokenBucket <- struct{}{} } requestRound++ + b.Logger.Sugar().Debugf("send token %d times", requestRound) } } }() diff --git a/pkg/pluginManager/netdns/agentExecuteTask.go b/pkg/pluginManager/netdns/agentExecuteTask.go index cd4e521c..b3410ce5 100644 --- a/pkg/pluginManager/netdns/agentExecuteTask.go +++ b/pkg/pluginManager/netdns/agentExecuteTask.go @@ -198,7 +198,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, } - var reportList []v1beta1.NetDNSTaskDetail + reportList := make([]v1beta1.NetDNSTaskDetail, 0, len(testTargetList)) var wg sync.WaitGroup var l lock.Mutex diff --git a/pkg/pluginManager/netreach/agentExecuteTask.go b/pkg/pluginManager/netreach/agentExecuteTask.go index 0586e6fa..18d1e3db 100644 --- a/pkg/pluginManager/netreach/agentExecuteTask.go +++ b/pkg/pluginManager/netreach/agentExecuteTask.go @@ -283,7 +283,7 @@ func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Contex } // ------------------------ implement for agent case and selected-pod case - var reportList []v1beta1.NetReachTaskDetail + reportList := make([]v1beta1.NetReachTaskDetail, 0, len(testTargetList)) var wg sync.WaitGroup var l lock.Mutex @@ -299,7 +299,7 @@ func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Contex EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, } logger.Sugar().Debugf("implement test %v, request %v ", t.Name, *d) - failureReason, itemReport := SendRequestAndReport(logger, t.Name, d, successCondition) + failureReason, itemReport := SendRequestAndReport(logger.With(zap.String("url", t.Url)), t.Name, d, successCondition) l.Lock() if len(failureReason) > 0 { finalfailureReason = fmt.Sprintf("test %v: %v", t.Name, failureReason) diff --git a/test/Makefile b/test/Makefile index 2c6ee0eb..133cbf27 100644 --- a/test/Makefile +++ b/test/Makefile @@ -159,8 +159,10 @@ deploy_project: HELM_OPTION+=" --set kdoctorAgent.ingress.enable=false " ; \ fi ; \ HELM_OPTION+=" --set feature.aggregateReport.enabled=true " ; \ - HELM_OPTION+=" --set kdoctorAgent.resources.requests.cpu=400m " ; \ - HELM_OPTION+=" --set kdoctorAgent.resources.limits.cpu=1600m " ; \ + HELM_OPTION+=" --set feature.nethttp_defaultConcurrency=10 " ; \ + HELM_OPTION+=" --set feature.netdns_defaultConcurrency=10 " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.requests.cpu=500m " ; \ + HELM_OPTION+=" --set kdoctorAgent.resources.limits.cpu=2000m " ; \ HELM_OPTION+=" --set kdoctorAgent.resources.requests.memory=512Mi " ; \ HELM_OPTION+=" --set kdoctorAgent.resources.limits.memory=2048Mi " ; \ HELM_OPTION+=" --set feature.aggregateReport.controller.reportHostPath=/var/run/kdoctor/controller " ; \ diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 7eef0935..d5733495 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -16,14 +16,14 @@ import ( "net" ) -var _ = Describe("testing appHttpHealth test ", Label("appHttpHealth"), Serial, func() { +var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), func() { // 2000ms is not stable on GitHub ci, so increased to 7000ms // issue : https://github.com/kdoctor-io/kdoctor/issues/222 // issue : https://github.com/kdoctor-io/kdoctor/issues/223 // issue : https://github.com/kdoctor-io/kdoctor/issues/165 // issue : https://github.com/kdoctor-io/kdoctor/issues/96 - var requestTimeout = 7000 - var successMean = int64(3000) + var requestTimeout = 15000 + var successMean = int64(7000) It("success http testing appHttpHealth method GET", Serial, Label("A00001", "A00011", "C00006", "E00002", "A00014", "E00017"), func() { var e error successRate := float64(1) From b24b89cdc837d3635047debf5d896b8f4985047e Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 11:27:16 +0800 Subject: [PATCH 27/41] add kdoctor icon Signed-off-by: ii2day --- charts/Chart.yaml | 1 + docs/images/kdoctor.svg | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 docs/images/kdoctor.svg diff --git a/charts/Chart.yaml b/charts/Chart.yaml index 77327bab..6cca4a55 100644 --- a/charts/Chart.yaml +++ b/charts/Chart.yaml @@ -1,6 +1,7 @@ apiVersion: v2 name: kdoctor home: "https://kdoctor-io.github.io/kdoctor" +icon: https://raw.githubusercontent.com/kdoctor-io/kdoctor/main/docs/images/kdoctor.svg # application or library type: application # no need to modify this version , CI will auto update it with /VERSION diff --git a/docs/images/kdoctor.svg b/docs/images/kdoctor.svg new file mode 100644 index 00000000..5c60832f --- /dev/null +++ b/docs/images/kdoctor.svg @@ -0,0 +1,14 @@ + + + Icon / Logo 20*20 / kdoctor + + + + + + + + + + + \ No newline at end of file From 27eb254a384c2bb65be4e6da9d0f597691ce9afe Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 10:15:24 +0800 Subject: [PATCH 28/41] fix httptrace data race && add e2e Signed-off-by: ii2day --- pkg/loadRequest/loadHttp/http_requester.go | 52 ++++---- test/docs/NetDns.md | 2 +- test/e2e/apphttphealth/apphttphealth_test.go | 65 +++++++++- test/e2e/netdns/netdns_test.go | 125 ++++++++++++++++++- test/e2e/netreach/netreach_test.go | 70 ++++++++++- 5 files changed, 281 insertions(+), 33 deletions(-) diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index b616dcfa..8361920a 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -242,41 +242,32 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { defer wg.Done() s := b.now() var size int64 - var dnsStart, connStart, resStart, reqStart, delayStart time.Duration - var dnsDuration, connDuration, resDuration, reqDuration, delayDuration time.Duration req := genRequest(b.Request, b.RequestBody) + var t0, t1, t2, t3, t4, t5, t6 time.Time trace := &httptrace.ClientTrace{ - DNSStart: func(info httptrace.DNSStartInfo) { - dnsStart = b.now() - }, - DNSDone: func(dnsInfo httptrace.DNSDoneInfo) { - dnsDuration = b.now() - dnsStart - }, - GetConn: func(h string) { - connStart = b.now() - }, - GotConn: func(connInfo httptrace.GotConnInfo) { - if !connInfo.Reused { - connDuration = b.now() - connStart + DNSStart: func(_ httptrace.DNSStartInfo) { t0 = time.Now() }, + DNSDone: func(_ httptrace.DNSDoneInfo) { t1 = time.Now() }, + ConnectStart: func(_, _ string) { + if t1.IsZero() { + t1 = time.Now() } - reqStart = b.now() }, - WroteRequest: func(w httptrace.WroteRequestInfo) { - reqDuration = b.now() - reqStart - delayStart = b.now() - }, - GotFirstResponseByte: func() { - delayDuration = b.now() - delayStart - resStart = b.now() + ConnectDone: func(net, addr string, err error) { + t2 = time.Now() + }, + GotConn: func(_ httptrace.GotConnInfo) { t3 = time.Now() }, + GotFirstResponseByte: func() { t4 = time.Now() }, + TLSHandshakeStart: func() { t5 = time.Now() }, + TLSHandshakeDone: func(_ tls.ConnectionState, _ error) { t6 = time.Now() }, } - req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) + req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace)) ctx, cancel := context.WithTimeout(req.Context(), time.Duration(b.Timeout)*time.Millisecond) defer cancel() req = req.WithContext(ctx) resp, err := c.Do(req) t := b.now() - resDuration = t - resStart + t7 := time.Now() finish := t - s var statusCode int if err == nil { @@ -285,12 +276,15 @@ func (b *Work) makeRequest(c *http.Client, wg *sync.WaitGroup) { statusCode = resp.StatusCode } else { statusCode = 0 + if t0.IsZero() { + t0 = t1 + } b.Logger.Sugar().Debugf("request err: %v", err) - b.Logger.Sugar().Debugf("connection setup(DNS lookup + Dial up) duration: %d", connDuration.Milliseconds()) - b.Logger.Sugar().Debugf("dns lookup duration: %d", dnsDuration.Milliseconds()) - b.Logger.Sugar().Debugf("request write duration: %d", reqDuration.Milliseconds()) - b.Logger.Sugar().Debugf("response read duration: %d", resDuration.Milliseconds()) - b.Logger.Sugar().Debugf("delay between response and request: %d", delayDuration.Milliseconds()) + b.Logger.Sugar().Debugf("dns lookup duration: %d", t1.Sub(t0).Milliseconds()) + b.Logger.Sugar().Debugf("tls handshake duration: %d", t6.Sub(t5).Milliseconds()) + b.Logger.Sugar().Debugf("tcp connection duration: %d", t2.Sub(t1).Milliseconds()) + b.Logger.Sugar().Debugf("server processing duration: %d", t4.Sub(t3).Milliseconds()) + b.Logger.Sugar().Debugf("content transfer duration: %d", t7.Sub(t4).Milliseconds()) } if b.ExpectStatusCode != nil { if statusCode != *b.ExpectStatusCode { diff --git a/test/docs/NetDns.md b/test/docs/NetDns.md index cf3e88f6..407a0315 100644 --- a/test/docs/NetDns.md +++ b/test/docs/NetDns.md @@ -5,4 +5,4 @@ | D00001 | Successfully testing Cluster Dns Server case | p1 | | done | | | D00002 | Successfully testing User Define Dns server case | p1 | | done | | | D00003 | Successfully testing User Define Dns server case use tcp protocol | p1 | | done | | -| D00004 | Successfully testing User Define Dns server case use tcp-tls protocol | p1 | | | | +| D00004 | Successfully testing User Define Dns server case use tcp-tls protocol | p1 | | done | | diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index d5733495..0ee37c9e 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -24,7 +24,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // issue : https://github.com/kdoctor-io/kdoctor/issues/96 var requestTimeout = 15000 var successMean = int64(7000) - It("success http testing appHttpHealth method GET", Serial, Label("A00001", "A00011", "C00006", "E00002", "A00014", "E00017"), func() { + It("success http testing appHttpHealth method GET", Serial, Label("A00001", "A00011", "C00006", "E00014", "A00014", "E00017"), func() { var e error successRate := float64(1) crontab := "0 1" @@ -815,4 +815,67 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) + + It("Successfully testing Task NetAppHttpHealthy Runtime DaemonSet Service creation", Serial, Label("E00002"), func() { + var e error + successRate := float64(1) + successMean := int64(3000) + crontab := "0 1" + appHttpHealthName := "apphttphealth-get" + tools.RandomName() + + appHttpHealth := new(v1beta1.AppHttpHealthy) + appHttpHealth.Name = appHttpHealthName + + // agent + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + appHttpHealth.Spec.AgentSpec = agentSpec + + // successCondition + successCondition := new(v1beta1.NetSuccessCondition) + successCondition.SuccessRate = &successRate + successCondition.MeanAccessDelayInMs = &successMean + appHttpHealth.Spec.SuccessCondition = successCondition + + // target + target := new(v1beta1.AppHttpHealthyTarget) + target.Method = "GET" + if net.ParseIP(testSvcIP).To4() == nil { + target.Host = fmt.Sprintf("http://[%s]:%d/?task=%s", testSvcIP, httpPort, appHttpHealthName) + } else { + target.Host = fmt.Sprintf("http://%s:%d?task=%s", testSvcIP, httpPort, appHttpHealthName) + } + target.EnableLatencyMetric = true + appHttpHealth.Spec.Target = target + + // request + request := new(v1beta1.NetHttpRequest) + request.PerRequestTimeoutInMS = requestTimeout + request.QPS = 10 + request.DurationInSecond = 10 + appHttpHealth.Spec.Request = request + + // Schedule + Schedule := new(v1beta1.SchedulePlan) + Schedule.Schedule = &crontab + Schedule.RoundNumber = 1 + Schedule.RoundTimeoutMinute = 1 + appHttpHealth.Spec.Schedule = Schedule + + e = frame.CreateResource(appHttpHealth) + Expect(e).NotTo(HaveOccurred(), "create appHttpHealth resource") + + e = common.CheckRuntime(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 60) + Expect(e).NotTo(HaveOccurred(), "check task runtime spec") + + e = common.WaitKdoctorTaskDone(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 120) + Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") + + success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) + Expect(e).NotTo(HaveOccurred(), "compare report and task") + Expect(success).To(BeTrue(), "compare report and task result") + + e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") + }) }) diff --git a/test/e2e/netdns/netdns_test.go b/test/e2e/netdns/netdns_test.go index c8762bb8..57e3bad7 100644 --- a/test/e2e/netdns/netdns_test.go +++ b/test/e2e/netdns/netdns_test.go @@ -20,7 +20,7 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { var targetDomain = "%s.kubernetes.default.svc.cluster.local" successMean := int64(1500) successRate := float64(1) - It("Successfully testing Cluster Dns Server case", Label("D00001", "C00005", "E00003", "E00015", "E00018"), Serial, func() { + It("Successfully testing Cluster Dns Server case", Label("D00001", "C00005", "E00015", "E00018"), Serial, func() { var e error crontab := "0 1" @@ -190,6 +190,61 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { Expect(success).To(BeTrue(), "compare report and task result") }) + It("Successfully testing User Define Dns server case use tcp-tls protocol", Serial, Label("D00004"), func() { + var e error + crontab := "0 1" + netDnsName := "netdns-e2e-" + tools.RandomName() + + netDns := new(v1beta1.Netdns) + netDns.Name = netDnsName + + // successCondition + successCondition := new(v1beta1.NetSuccessCondition) + successCondition.SuccessRate = &successRate + successCondition.MeanAccessDelayInMs = &successMean + netDns.Spec.SuccessCondition = successCondition + + // target + target := new(v1beta1.NetDnsTarget) + targetDnsUser := new(v1beta1.NetDnsTargetUserSpec) + targetDnsUser.Server = &testSvcIP + port := 853 + targetDnsUser.Port = &port + target.NetDnsTargetUser = targetDnsUser + target.EnableLatencyMetric = true + netDns.Spec.Target = target + + // request + request := new(v1beta1.NetdnsRequest) + request.PerRequestTimeoutInMS = 2000 + request.QPS = 10 + request.DurationInSecond = 10 + request.Domain = fmt.Sprintf(targetDomain, netDnsName) + protocol := "tcp-tls" + request.Protocol = &protocol + netDns.Spec.Request = request + + // Schedule + Schedule := new(v1beta1.SchedulePlan) + Schedule.Schedule = &crontab + Schedule.RoundNumber = 1 + Schedule.RoundTimeoutMinute = 1 + netDns.Spec.Schedule = Schedule + + e = frame.CreateResource(netDns) + Expect(e).NotTo(HaveOccurred(), "create netDns resource") + + e = common.CheckRuntime(frame, netDns, pluginManager.KindNameNetdns, 60) + Expect(e).NotTo(HaveOccurred(), "check task runtime spec") + + e = common.WaitKdoctorTaskDone(frame, netDns, pluginManager.KindNameNetdns, 120) + Expect(e).NotTo(HaveOccurred(), "wait netDns task finish") + + success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, testPodIPs, reportNum, netDns) + Expect(e).NotTo(HaveOccurred(), "compare report and task") + Expect(success).To(BeTrue(), "compare report and task result") + }) + It("Successfully testing NetDns crontab case", Label("C00002"), Serial, func() { var e error crontab := "* * * * *" @@ -315,4 +370,72 @@ var _ = Describe("testing netDns ", Label("netDns"), func() { Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) + + It("Successfully testing Task NetDns Runtime DaemonSet Service creation", Serial, Label("E00003"), func() { + var e error + successRate := float64(1) + successMean := int64(1500) + crontab := "0 1" + netDnsName := "netdns-e2e-" + tools.RandomName() + + // netdns + netDns := new(v1beta1.Netdns) + netDns.Name = netDnsName + + // agentSpec + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + netDns.Spec.AgentSpec = agentSpec + + // successCondition + successCondition := new(v1beta1.NetSuccessCondition) + successCondition.SuccessRate = &successRate + successCondition.MeanAccessDelayInMs = &successMean + netDns.Spec.SuccessCondition = successCondition + + // target + target := new(v1beta1.NetDnsTarget) + targetDns := new(v1beta1.NetDnsTargetDnsSpec) + targetDns.TestIPv4 = &common.TestIPv4 + targetDns.TestIPv6 = &common.TestIPv6 + targetDns.ServiceName = &KubeDnsName + targetDns.ServiceNamespace = &KubeDnsNamespace + target.NetDnsTargetDns = targetDns + target.EnableLatencyMetric = true + netDns.Spec.Target = target + + // request + request := new(v1beta1.NetdnsRequest) + request.PerRequestTimeoutInMS = 1000 + request.QPS = 10 + request.DurationInSecond = 10 + request.Domain = "kubernetes.default.svc.cluster.local" + protocol := "udp" + request.Protocol = &protocol + netDns.Spec.Request = request + + // Schedule + Schedule := new(v1beta1.SchedulePlan) + Schedule.Schedule = &crontab + Schedule.RoundNumber = 1 + Schedule.RoundTimeoutMinute = 1 + netDns.Spec.Schedule = Schedule + + e = frame.CreateResource(netDns) + Expect(e).NotTo(HaveOccurred(), "create netDns resource") + + e = common.CheckRuntime(frame, netDns, pluginManager.KindNameNetdns, 60) + Expect(e).NotTo(HaveOccurred(), "check task runtime spec") + + e = common.WaitKdoctorTaskDone(frame, netDns, pluginManager.KindNameNetdns, 120) + Expect(e).NotTo(HaveOccurred(), "wait netDns task finish") + + success, e := common.CompareResult(frame, netDnsName, pluginManager.KindNameNetdns, []string{}, reportNum, netDns) + Expect(e).NotTo(HaveOccurred(), "compare report and task") + Expect(success).NotTo(BeFalse(), "compare report and task result") + + e = common.CheckRuntimeDeadLine(frame, netDnsName, pluginManager.KindNameNetdns, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") + + }) }) diff --git a/test/e2e/netreach/netreach_test.go b/test/e2e/netreach/netreach_test.go index a97a97c7..2ae69b01 100644 --- a/test/e2e/netreach/netreach_test.go +++ b/test/e2e/netreach/netreach_test.go @@ -19,7 +19,7 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { var requestTimeout = 15000 var successRate = float64(1) var successMean = int64(7000) - It("success testing netReach", Label("B00001", "C00004", "E00001", "E00013", "E00016"), Serial, func() { + It("success testing netReach", Label("B00001", "C00004", "E00013", "E00016"), Serial, func() { var e error crontab := "0 1" netReachName := "netreach-" + tools.RandomName() @@ -208,4 +208,72 @@ var _ = Describe("testing netReach ", Label("netReach"), func() { e = common.CheckRuntimeDeadLine(frame, netReachName, pluginManager.KindNameNetReach, 120) Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) + + It("Successfully testing Task NetReach Runtime DaemonSet Service Ingress creation ", Label("E00001"), Serial, func() { + var e error + crontab := "0 1" + netReachName := "netreach-" + tools.RandomName() + + netReach := new(v1beta1.NetReach) + netReach.Name = netReachName + + // agentSpec + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) + netReach.Spec.AgentSpec = agentSpec + + // successCondition + successCondition := new(v1beta1.NetSuccessCondition) + successCondition.SuccessRate = &successRate + successCondition.MeanAccessDelayInMs = &successMean + netReach.Spec.SuccessCondition = successCondition + enable := true + disable := false + // target + target := new(v1beta1.NetReachTarget) + if !common.TestIPv4 && common.TestIPv6 { + target.Ingress = &disable + } else { + target.Ingress = &enable + } + target.LoadBalancer = &enable + target.ClusterIP = &enable + target.Endpoint = &enable + target.NodePort = &enable + target.MultusInterface = &disable + target.IPv4 = &common.TestIPv4 + target.IPv6 = &common.TestIPv6 + target.EnableLatencyMetric = true + netReach.Spec.Target = target + + // request + request := new(v1beta1.NetHttpRequest) + request.PerRequestTimeoutInMS = requestTimeout + request.QPS = 10 + request.DurationInSecond = 10 + netReach.Spec.Request = request + + // Schedule + Schedule := new(v1beta1.SchedulePlan) + Schedule.Schedule = &crontab + Schedule.RoundNumber = 1 + Schedule.RoundTimeoutMinute = 1 + netReach.Spec.Schedule = Schedule + + e = frame.CreateResource(netReach) + Expect(e).NotTo(HaveOccurred(), "create netReach resource") + + e = common.CheckRuntime(frame, netReach, pluginManager.KindNameNetReach, 60) + Expect(e).NotTo(HaveOccurred(), "check task runtime spec") + + e = common.WaitKdoctorTaskDone(frame, netReach, pluginManager.KindNameNetReach, 120) + Expect(e).NotTo(HaveOccurred(), "wait netReach task finish") + + success, e := common.CompareResult(frame, netReachName, pluginManager.KindNameNetReach, []string{}, reportNum, netReach) + Expect(e).NotTo(HaveOccurred(), "compare report and task") + Expect(success).To(BeTrue(), "compare report and task result") + + e = common.CheckRuntimeDeadLine(frame, netReachName, pluginManager.KindNameNetReach, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") + }) }) From 42a1bb19741b4b2cf37689ec31dd454b16c7c773 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 15:55:38 +0800 Subject: [PATCH 29/41] fix report wrong time Signed-off-by: ii2day --- pkg/apiserver/registry/kdoctor/kdoctorreport/etcd.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/apiserver/registry/kdoctor/kdoctorreport/etcd.go b/pkg/apiserver/registry/kdoctor/kdoctorreport/etcd.go index e9bc3d7b..e07a2423 100644 --- a/pkg/apiserver/registry/kdoctor/kdoctorreport/etcd.go +++ b/pkg/apiserver/registry/kdoctor/kdoctorreport/etcd.go @@ -111,7 +111,7 @@ func (p kdoctorReportStorage) Get(ctx context.Context, key string, opts storage. var taskStatus *crd.TaskStatus var taskType string - + var creationTimestamp metav1.Time _, name, err := NamespaceAndNameFromKey(key, false) if nil != err { return err @@ -128,6 +128,7 @@ func (p kdoctorReportStorage) Get(ctx context.Context, key string, opts storage. } else { fmt.Printf("succeed to get NetDNS %s\n", name) taskStatus = netdns.Status.DeepCopy() + creationTimestamp = netdns.CreationTimestamp taskType = v1beta1.NetDNSTaskName } @@ -141,6 +142,7 @@ func (p kdoctorReportStorage) Get(ctx context.Context, key string, opts storage. } else { fmt.Printf("succeed to get NetReachHealthy %s\n", name) taskStatus = netReach.Status.DeepCopy() + creationTimestamp = netReach.CreationTimestamp taskType = v1beta1.NetReachTaskName } @@ -154,6 +156,7 @@ func (p kdoctorReportStorage) Get(ctx context.Context, key string, opts storage. } else { fmt.Printf("succeed to get HttpAppHealthy %s\n", name) taskStatus = appHttpHealthy.Status.DeepCopy() + creationTimestamp = appHttpHealthy.CreationTimestamp taskType = v1beta1.AppHttpHealthyTaskName } @@ -199,6 +202,7 @@ func (p kdoctorReportStorage) Get(ctx context.Context, key string, opts storage. } kdoctorReport := objPtr.(*v1beta1.KdoctorReport) + kdoctorReport.CreationTimestamp = creationTimestamp kdoctorReport.Spec = v1beta1.KdoctorReportSpec{ TaskName: name, TaskType: taskType, @@ -402,6 +406,7 @@ func (p kdoctorReportStorage) getNetDNSKdoctorReports(ctx context.Context, fileN kdoctorReport := &v1beta1.KdoctorReport{} kdoctorReport.Name = tmpNetDNS.Name + kdoctorReport.CreationTimestamp = tmpNetDNS.CreationTimestamp kdoctorReport.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{ Group: v1beta1.GroupName, Version: v1beta1.V1betaVersion, @@ -475,6 +480,7 @@ func (p kdoctorReportStorage) getHttpAppHealthyReports(ctx context.Context, file kdoctorReport := &v1beta1.KdoctorReport{} kdoctorReport.Name = tmpHttpAppHealthy.Name + kdoctorReport.CreationTimestamp = tmpHttpAppHealthy.CreationTimestamp kdoctorReport.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{ Group: v1beta1.GroupName, Version: v1beta1.V1betaVersion, @@ -548,6 +554,7 @@ func (p kdoctorReportStorage) getNetReachHealthyReports(ctx context.Context, fil kdoctorReport := &v1beta1.KdoctorReport{} kdoctorReport.Name = tmpNetReachHealthy.Name + kdoctorReport.CreationTimestamp = tmpNetReachHealthy.CreationTimestamp kdoctorReport.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{ Group: v1beta1.GroupName, Version: v1beta1.V1betaVersion, From a3b32c46389f78106d0f50afc8aef73da317658f Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 14:49:07 +0800 Subject: [PATCH 30/41] add dynamic request worker number && Adjust configuration parameter Signed-off-by: ii2day --- charts/README.md | 17 ++++----- charts/values.yaml | 35 ++++++++++--------- docs/usage/debug-zh_CN.md | 4 +-- docs/usage/debug.md | 4 +-- pkg/loadRequest/loadDns/dns.go | 8 ++--- pkg/loadRequest/loadDns/dns_suite_test.go | 3 -- pkg/loadRequest/loadDns/dns_test.go | 5 +++ pkg/loadRequest/loadHttp/http.go | 6 ++-- pkg/loadRequest/loadHttp/http_requester.go | 2 +- pkg/loadRequest/loadHttp/http_suite_test.go | 3 -- pkg/loadRequest/loadHttp/http_test.go | 2 ++ .../apphttphealthy/agentExecuteTask.go | 10 ++++++ pkg/pluginManager/apphttphealthy/webhook.go | 10 +++--- pkg/pluginManager/netdns/agentExecuteTask.go | 14 ++++++++ pkg/pluginManager/netdns/webhook.go | 4 +-- .../netreach/agentExecuteTask.go | 9 +++++ pkg/pluginManager/netreach/webhook.go | 10 +++--- pkg/types/types.go | 34 +++++++++++------- 18 files changed, 114 insertions(+), 66 deletions(-) diff --git a/charts/README.md b/charts/README.md index d4a967e4..464bab7e 100644 --- a/charts/README.md +++ b/charts/README.md @@ -25,14 +25,15 @@ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------ | | `feature.enableIPv4` | enable ipv4 | `true` | | `feature.enableIPv6` | enable ipv6 | `true` | -| `feature.nethttp_defaultRequest_Qps` | qps for kind nethttp | `10` | -| `feature.nethttp_defaultRequest_MaxQps` | qps for kind nethttp | `100` | -| `feature.nethttp_defaultConcurrency` | concurrency for kind nethttp | `50` | -| `feature.nethttp_defaultMaxIdleConnsPerHost` | max idle connect for kind nethttp | `50` | -| `feature.nethttp_defaultRequest_DurationInSecond` | Duration In Second for kind nethttp | `2` | -| `feature.nethttp_defaultRequest_PerRequestTimeoutInMS` | PerRequest Timeout In MS for kind nethttp | `500` | -| `feature.nethttp_defaultFail_MeanDelayInMs` | mean delay in ms for kind nethttp | `2000` | -| `feature.netdns_defaultConcurrency` | concurrency for kind netdns | `50` | +| `feature.netReachRequestMaxQPS` | qps for kind NetReach | `10` | +| `feature.netReachMaxConcurrency` | concurrency for kind NetReach | `20` | +| `feature.appHttpHealthyMaxConcurrency` | concurrency for kind AppHttpHealthy | `10` | +| `feature.appHttpHealthyRequestMaxQPS` | qps for kind AppHttpHealthy | `100` | +| `feature.netHttpDefaultMaxIdleConnsPerHost` | max idle connect for kind NetHttp | `50` | +| `feature.netHttpDefaultRequestDurationInSecond` | Duration In Second for kind NetHttp | `2` | +| `feature.netHttpDefaultRequestPerRequestTimeoutInMS` | PerRequest Timeout In MS for kind NetHttp | `500` | +| `feature.netDnsMaxConcurrency` | concurrency for kind NetDns | `20` | +| `feature.netDnsRequestMaxQPS` | qps for kind NetDns | `100` | | `feature.agentDefaultTerminationGracePeriodMinutes` | agent termination after minutes | `60` | | `feature.taskPollIntervalInSecond` | the interval to poll the task in controller and agent pod | `5` | | `feature.multusPodAnnotationKey` | the multus annotation key for ip status | `k8s.v1.cni.cncf.io/networks-status` | diff --git a/charts/values.yaml b/charts/values.yaml index 9289e420..3670b615 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -38,29 +38,32 @@ feature: ## @param feature.enableIPv6 enable ipv6 enableIPv6: true - ## @param feature.nethttp_defaultRequest_Qps qps for kind nethttp - nethttp_defaultRequest_Qps: 10 + ## @param feature.netReachRequestMaxQPS qps for kind NetReach + netReachRequestMaxQPS: 10 - ## @param feature.nethttp_defaultRequest_MaxQps qps for kind nethttp - nethttp_defaultRequest_MaxQps: 100 + ## @param feature.netReachMaxConcurrency concurrency for kind NetReach + netReachMaxConcurrency: 20 - ## @param feature.nethttp_defaultConcurrency concurrency for kind nethttp - nethttp_defaultConcurrency: 50 + ## @param feature.appHttpHealthyMaxConcurrency concurrency for kind AppHttpHealthy + appHttpHealthyMaxConcurrency: 10 - ## @param feature.nethttp_defaultMaxIdleConnsPerHost max idle connect for kind nethttp - nethttp_defaultMaxIdleConnsPerHost: 50 + ## @param feature.appHttpHealthyRequestMaxQPS qps for kind AppHttpHealthy + appHttpHealthyRequestMaxQPS: 100 - ## @param feature.nethttp_defaultRequest_DurationInSecond Duration In Second for kind nethttp - nethttp_defaultRequest_DurationInSecond: 2 + ## @param feature.netHttpDefaultMaxIdleConnsPerHost max idle connect for kind NetHttp + netHttpDefaultMaxIdleConnsPerHost: 50 - ## @param feature.nethttp_defaultRequest_PerRequestTimeoutInMS PerRequest Timeout In MS for kind nethttp - nethttp_defaultRequest_PerRequestTimeoutInMS: 500 + ## @param feature.netHttpDefaultRequestDurationInSecond Duration In Second for kind NetHttp + netHttpDefaultRequestDurationInSecond: 2 - ## @param feature.nethttp_defaultFail_MeanDelayInMs mean delay in ms for kind nethttp - nethttp_defaultFail_MeanDelayInMs: 2000 + ## @param feature.netHttpDefaultRequestPerRequestTimeoutInMS PerRequest Timeout In MS for kind NetHttp + netHttpDefaultRequestPerRequestTimeoutInMS: 500 - ## @param feature.netdns_defaultConcurrency concurrency for kind netdns - netdns_defaultConcurrency: 50 + ## @param feature.netDnsMaxConcurrency concurrency for kind NetDns + netDnsMaxConcurrency: 20 + + ## @param feature.netDnsRequestMaxQPS qps for kind NetDns + netDnsRequestMaxQPS: 100 ## @param feature.agentDefaultTerminationGracePeriodMinutes agent termination after minutes agentDefaultTerminationGracePeriodMinutes: 60 diff --git a/docs/usage/debug-zh_CN.md b/docs/usage/debug-zh_CN.md index bcd04244..0f1f418f 100644 --- a/docs/usage/debug-zh_CN.md +++ b/docs/usage/debug-zh_CN.md @@ -4,12 +4,12 @@ **Q: 想要使用更高的 QPS 应该如何设置?** * A: 当 QPS 设置过大,会导致服务器资源占用过高,影响业务。为了防止在生产坏境出现误操作。kdoctor 在 webhook 中添加了 QPS 的检查。如果您想使用更高的 QPS -可通过参数设置 QPS 限制 `--set feature.nethttp_defaultRequest_MaxQps=1000`,也可以通过 kdoctor 的 configmap 中去更改 `nethttp_defaultRequest_MaxQps` , +可通过参数设置 QPS 限制 `--set feature.appHttpHealthyRequestMaxQPS=1000`,`--set feature.netReachRequestMaxQPS=1000`,`--set feature.netDnsRequestMaxQPS=1000`,也可以通过 kdoctor 的 configmap 中去更改 , 并重启 kdoctor 的相关 pod 重新加载 configmap。 **Q: 为什么我的任务无法达到期望的 QPS ?** * A:无法达到 QPS 的期望原因有很多主要分为以下几种原因: - * 并发 worker 设置过低,kdoctor 可通过设置参数调整并发数 `--set feature.nethttp_defaultConcurrency=50`,`--set feature.netdns_defaultConcurrency=50`。 + * 并发 worker 设置过低,kdoctor 可通过设置参数调整并发数 `--set feature.netReachMaxConcurrency=50`,`--set feature.appHttpHealthyMaxConcurrency=50`,`--set feature.netDnsMaxConcurrency=50`。 * kdoctor agent 分配资源不充足,可通过 kdoctor 的聚合报告`kubectl get kdoctorreport `查看任务消耗的 cpu 与 内存使用量,确定 kdoctor agent 资源分配是否充足。 ```shell ~kubectl get kdoctorreport test-task -oyaml diff --git a/docs/usage/debug.md b/docs/usage/debug.md index 965a3f5b..56151603 100644 --- a/docs/usage/debug.md +++ b/docs/usage/debug.md @@ -6,12 +6,12 @@ **Q: How to achieve higher QPS?** * A: When the QPS setting is too high, it can result in excessive server resource utilization, impacting business operations. To prevent accidental misconfiguration in production environments, kdoctor has added QPS checks in the webhook. -* If you wish to use a higher QPS, you can set the QPS limit using the parameter `--set feature.nethttp_defaultRequest_MaxQps=1000`,You can also modify it through the configmap in kdoctor `nethttp_defaultRequest_MaxQps`, +* If you wish to use a higher QPS, you can set the QPS limit using the parameter `--set feature.appHttpHealthyRequestMaxQPS=1000`,`--set feature.netReachRequestMaxQPS=1000`,`--set feature.netDnsRequestMaxQPS=1000`,You can also modify it through the configmap in kdoctor, And restart the relevant pods of kdoctor to reload the configmap. **Q: Why is my task unable to achieve the desired QPS ?** * A:There are several reasons why the expected QPS cannot be achieved, primarily categorized into the following reasons: - * The concurrency worker setting is too low. kdoctor can adjust the concurrency by setting the parameters `--set feature.nethttp_defaultConcurrency=50` and `--set feature.netdns_defaultConcurrency=50`. + * The concurrency worker setting is too low. kdoctor can adjust the concurrency by setting the parameters `--set feature.netReachMaxConcurrency=50`,`--set feature.appHttpHealthyMaxConcurrency=50`,`--set feature.netDnsMaxConcurrency=50`. * The kdoctor agent may have insufficient resource allocation. You can use the kdoctor aggregate report `kubectl get kdoctorreport` to check the CPU and memory usage of the task. This will help you determine if the resource allocation for the kdoctor agent is sufficient. ```shell ~kubectl get kdoctorreport test-task -oyaml diff --git a/pkg/loadRequest/loadDns/dns.go b/pkg/loadRequest/loadDns/dns.go index e41fddc5..318a645b 100644 --- a/pkg/loadRequest/loadDns/dns.go +++ b/pkg/loadRequest/loadDns/dns.go @@ -28,8 +28,6 @@ import ( "github.com/miekg/dns" "go.uber.org/zap" "time" - - config "github.com/kdoctor-io/kdoctor/pkg/types" ) type RequestProtocol string @@ -52,6 +50,7 @@ type DnsRequestData struct { DnsServerAddr string PerRequestTimeoutInMs int Qps int + Workers int DurationInSecond int EnableLatencyMetric bool } @@ -68,11 +67,12 @@ func DnsRequest(logger *zap.Logger, reqData *DnsRequestData) (result *v1beta1.DN reqData.TargetDomain = dns.Fqdn(reqData.TargetDomain) logger.Sugar().Debugf("convert target domain to fqdn %v", reqData.TargetDomain) } - duration := time.Duration(reqData.DurationInSecond) * time.Second + logger.Sugar().Infof("http request Concurrency=%d", reqData.Workers) + w := &Work{ - Concurrency: config.AgentConfig.Configmap.NetdnsDefaultConcurrency, + Concurrency: reqData.Workers, RequestTimeSecond: reqData.DurationInSecond, QPS: reqData.Qps, Timeout: reqData.PerRequestTimeoutInMs, diff --git a/pkg/loadRequest/loadDns/dns_suite_test.go b/pkg/loadRequest/loadDns/dns_suite_test.go index fec4655f..d874716c 100644 --- a/pkg/loadRequest/loadDns/dns_suite_test.go +++ b/pkg/loadRequest/loadDns/dns_suite_test.go @@ -7,8 +7,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - config "github.com/kdoctor-io/kdoctor/pkg/types" ) func TestLoadDns(t *testing.T) { @@ -18,5 +16,4 @@ func TestLoadDns(t *testing.T) { var _ = BeforeSuite(func() { // nothing to do - config.AgentConfig.Configmap.NetdnsDefaultConcurrency = 10 }) diff --git a/pkg/loadRequest/loadDns/dns_test.go b/pkg/loadRequest/loadDns/dns_test.go index f8f86a46..f9a503f9 100644 --- a/pkg/loadRequest/loadDns/dns_test.go +++ b/pkg/loadRequest/loadDns/dns_test.go @@ -27,6 +27,7 @@ var _ = Describe("test dns ", Label("dns"), func() { PerRequestTimeoutInMs: 5000, DurationInSecond: 10, Qps: 10, + Workers: 10, } log := logger.NewStdoutLogger("debug", "test") @@ -57,6 +58,7 @@ var _ = Describe("test dns ", Label("dns"), func() { PerRequestTimeoutInMs: 5000, DurationInSecond: 10, Qps: 10, + Workers: 10, EnableLatencyMetric: true, } @@ -88,6 +90,7 @@ var _ = Describe("test dns ", Label("dns"), func() { PerRequestTimeoutInMs: 5000, DurationInSecond: 10, Qps: 10, + Workers: 10, } log := logger.NewStdoutLogger("debug", "test") @@ -117,6 +120,7 @@ var _ = Describe("test dns ", Label("dns"), func() { PerRequestTimeoutInMs: 5000, DurationInSecond: 10, Qps: 10, + Workers: 10, } log := logger.NewStdoutLogger("debug", "test") @@ -146,6 +150,7 @@ var _ = Describe("test dns ", Label("dns"), func() { PerRequestTimeoutInMs: 5000, DurationInSecond: 10, Qps: 10, + Workers: 10, } log := logger.NewStdoutLogger("debug", "test") diff --git a/pkg/loadRequest/loadHttp/http.go b/pkg/loadRequest/loadHttp/http.go index 02ae97d1..57e260d2 100644 --- a/pkg/loadRequest/loadHttp/http.go +++ b/pkg/loadRequest/loadHttp/http.go @@ -31,7 +31,6 @@ import ( "go.uber.org/zap" "github.com/kdoctor-io/kdoctor/pkg/k8s/apis/system/v1beta1" - config "github.com/kdoctor-io/kdoctor/pkg/types" ) type HttpMethod string @@ -51,6 +50,7 @@ type HttpRequestData struct { Method HttpMethod Url string Qps int + Workers int PerRequestTimeoutMS int RequestTimeSecond int Header map[string]string @@ -73,12 +73,12 @@ func HttpRequest(logger *zap.Logger, reqData *HttpRequestData) *v1beta1.HttpMetr req.Header.Set(k, v) } - logger.Sugar().Infof("http request Concurrency=%d", config.AgentConfig.Configmap.NethttpDefaultConcurrency) + logger.Sugar().Infof("http request Concurrency=%d", reqData.Workers) w := &Work{ Request: req, RequestTimeSecond: reqData.RequestTimeSecond, - Concurrency: config.AgentConfig.Configmap.NethttpDefaultConcurrency, + Concurrency: reqData.Workers, QPS: reqData.Qps, Timeout: reqData.PerRequestTimeoutMS, DisableCompression: reqData.DisableCompression, diff --git a/pkg/loadRequest/loadHttp/http_requester.go b/pkg/loadRequest/loadHttp/http_requester.go index b616dcfa..495f6774 100644 --- a/pkg/loadRequest/loadHttp/http_requester.go +++ b/pkg/loadRequest/loadHttp/http_requester.go @@ -316,7 +316,7 @@ func (b *Work) runWorker() { InsecureSkipVerify: true, ServerName: b.Request.Host, }, - MaxIdleConnsPerHost: config.AgentConfig.Configmap.NethttpDefaultMaxIdleConnsPerHost, + MaxIdleConnsPerHost: config.AgentConfig.Configmap.NetHttpDefaultMaxIdleConnsPerHost, DisableCompression: b.DisableCompression, DisableKeepAlives: b.DisableKeepAlives, Proxy: http.ProxyURL(b.ProxyAddr), diff --git a/pkg/loadRequest/loadHttp/http_suite_test.go b/pkg/loadRequest/loadHttp/http_suite_test.go index 967891ae..53f9eebd 100644 --- a/pkg/loadRequest/loadHttp/http_suite_test.go +++ b/pkg/loadRequest/loadHttp/http_suite_test.go @@ -7,8 +7,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - config "github.com/kdoctor-io/kdoctor/pkg/types" ) func TestLoadHttp(t *testing.T) { @@ -18,5 +16,4 @@ func TestLoadHttp(t *testing.T) { var _ = BeforeSuite(func() { // nothing to do - config.AgentConfig.Configmap.NethttpDefaultConcurrency = 10 }) diff --git a/pkg/loadRequest/loadHttp/http_test.go b/pkg/loadRequest/loadHttp/http_test.go index f438b9ae..de5c44df 100644 --- a/pkg/loadRequest/loadHttp/http_test.go +++ b/pkg/loadRequest/loadHttp/http_test.go @@ -25,6 +25,7 @@ var _ = Describe("test http ", Label("http"), func() { PerRequestTimeoutMS: 10000, RequestTimeSecond: 10, Qps: 10, + Workers: 10, Header: header, } log := logger.NewStdoutLogger("debug", "test") @@ -51,6 +52,7 @@ var _ = Describe("test http ", Label("http"), func() { RequestTimeSecond: 10, EnableLatencyMetric: true, Qps: 10, + Workers: 10, Header: header, } log := logger.NewStdoutLogger("debug", "test") diff --git a/pkg/pluginManager/apphttphealthy/agentExecuteTask.go b/pkg/pluginManager/apphttphealthy/agentExecuteTask.go index 547a6b2e..7a0ffb28 100644 --- a/pkg/pluginManager/apphttphealthy/agentExecuteTask.go +++ b/pkg/pluginManager/apphttphealthy/agentExecuteTask.go @@ -16,6 +16,7 @@ import ( "github.com/kdoctor-io/kdoctor/pkg/pluginManager/types" "github.com/kdoctor-io/kdoctor/pkg/resource" "github.com/kdoctor-io/kdoctor/pkg/runningTask" + config "github.com/kdoctor-io/kdoctor/pkg/types" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/pointer" @@ -91,6 +92,14 @@ func (s *PluginAppHttpHealthy) AgentExecuteTask(logger *zap.Logger, ctx context. target := instance.Spec.Target request := instance.Spec.Request successCondition := instance.Spec.SuccessCondition + + var workers int + if request.QPS > config.AgentConfig.Configmap.AppHttpHealthyMaxConcurrency { + workers = config.AgentConfig.Configmap.AppHttpHealthyMaxConcurrency + } else { + workers = request.QPS + } + logger.Sugar().Infof("load test custom target: Method=%v, Url=%v , qps=%v, PerRequestTimeout=%vs, Duration=%vs", target.Method, target.Host, request.QPS, request.PerRequestTimeoutInMS, request.DurationInSecond) task.TargetType = "HttpAppHealthy" task.TargetNumber = 1 @@ -98,6 +107,7 @@ func (s *PluginAppHttpHealthy) AgentExecuteTask(logger *zap.Logger, ctx context. Method: loadHttp.HttpMethod(target.Method), Url: target.Host, Qps: request.QPS, + Workers: workers, PerRequestTimeoutMS: request.PerRequestTimeoutInMS, RequestTimeSecond: request.DurationInSecond, Http2: target.Http2, diff --git a/pkg/pluginManager/apphttphealthy/webhook.go b/pkg/pluginManager/apphttphealthy/webhook.go index 7db68ea7..a71666d9 100644 --- a/pkg/pluginManager/apphttphealthy/webhook.go +++ b/pkg/pluginManager/apphttphealthy/webhook.go @@ -39,9 +39,9 @@ func (s *PluginAppHttpHealthy) WebhookMutating(logger *zap.Logger, ctx context.C if req.Spec.Request == nil { m := &crd.NetHttpRequest{ - DurationInSecond: types.ControllerConfig.Configmap.NethttpDefaultRequestDurationInSecond, - QPS: types.ControllerConfig.Configmap.NethttpDefaultRequestQps, - PerRequestTimeoutInMS: types.ControllerConfig.Configmap.NethttpDefaultRequestPerRequestTimeoutInMS, + DurationInSecond: types.ControllerConfig.Configmap.NetHttpDefaultRequestDurationInSecond, + QPS: types.ControllerConfig.Configmap.NetHttpDefaultRequestQPS, + PerRequestTimeoutInMS: types.ControllerConfig.Configmap.NetHttpDefaultRequestPerRequestTimeoutInMS, } req.Spec.Request = m logger.Sugar().Debugf("set default Request for HttpAppHealthy %v", req.Name) @@ -94,8 +94,8 @@ func (s *PluginAppHttpHealthy) WebhookValidateCreate(logger *zap.Logger, ctx con // validate request if true { - if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps { - s := fmt.Sprintf("HttpAppHealthy %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps) + if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.AppHttpHealthyRequestMaxQPS { + s := fmt.Sprintf("HttpAppHealthy %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.AppHttpHealthyRequestMaxQPS) logger.Error(s) return apierrors.NewBadRequest(s) } diff --git a/pkg/pluginManager/netdns/agentExecuteTask.go b/pkg/pluginManager/netdns/agentExecuteTask.go index b3410ce5..e13abd8c 100644 --- a/pkg/pluginManager/netdns/agentExecuteTask.go +++ b/pkg/pluginManager/netdns/agentExecuteTask.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "github.com/kdoctor-io/kdoctor/pkg/runningTask" + config "github.com/kdoctor-io/kdoctor/pkg/types" "net" "strconv" "sync" @@ -95,6 +96,13 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, logger.Sugar().Infof("plugin implement task round, instance=%+v", instance) + var workers int + if instance.Spec.Request.QPS > config.AgentConfig.Configmap.NetDnsMaxConcurrency { + workers = config.AgentConfig.Configmap.NetDnsMaxConcurrency + } else { + workers = instance.Spec.Request.QPS + } + var testTargetList []*testTarget var server string @@ -110,6 +118,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) @@ -121,6 +130,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) @@ -146,6 +156,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) @@ -157,6 +168,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) @@ -178,6 +190,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) @@ -189,6 +202,7 @@ func (s *PluginNetDns) AgentExecuteTask(logger *zap.Logger, ctx context.Context, DnsServerAddr: server, PerRequestTimeoutInMs: instance.Spec.Request.PerRequestTimeoutInMS, Qps: instance.Spec.Request.QPS, + Workers: workers, DurationInSecond: instance.Spec.Request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, }}) diff --git a/pkg/pluginManager/netdns/webhook.go b/pkg/pluginManager/netdns/webhook.go index d5a8b9a0..922b4bd6 100644 --- a/pkg/pluginManager/netdns/webhook.go +++ b/pkg/pluginManager/netdns/webhook.go @@ -62,8 +62,8 @@ func (s *PluginNetDns) WebhookValidateCreate(logger *zap.Logger, ctx context.Con // validate request if true { - if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps { - s := fmt.Sprintf("netdns %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps) + if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NetDnsRequestMaxQPS { + s := fmt.Sprintf("netdns %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NetDnsRequestMaxQPS) logger.Error(s) return apierrors.NewBadRequest(s) } diff --git a/pkg/pluginManager/netreach/agentExecuteTask.go b/pkg/pluginManager/netreach/agentExecuteTask.go index 18d1e3db..1868e404 100644 --- a/pkg/pluginManager/netreach/agentExecuteTask.go +++ b/pkg/pluginManager/netreach/agentExecuteTask.go @@ -95,6 +95,14 @@ func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Contex request := instance.Spec.Request successCondition := instance.Spec.SuccessCondition runtimeResource := instance.Status.Resource + + var workers int + if request.QPS > config.AgentConfig.Configmap.NetReachMaxConcurrency { + workers = config.AgentConfig.Configmap.NetReachMaxConcurrency + } else { + workers = request.QPS + } + testTargetList := []*TestTarget{} // test kdoctor agent @@ -294,6 +302,7 @@ func (s *PluginNetReach) AgentExecuteTask(logger *zap.Logger, ctx context.Contex Method: t.Method, Url: t.Url, Qps: request.QPS, + Workers: workers, PerRequestTimeoutMS: request.PerRequestTimeoutInMS, RequestTimeSecond: request.DurationInSecond, EnableLatencyMetric: instance.Spec.Target.EnableLatencyMetric, diff --git a/pkg/pluginManager/netreach/webhook.go b/pkg/pluginManager/netreach/webhook.go index 676e1fed..763a4b21 100644 --- a/pkg/pluginManager/netreach/webhook.go +++ b/pkg/pluginManager/netreach/webhook.go @@ -94,9 +94,9 @@ func (s *PluginNetReach) WebhookMutating(logger *zap.Logger, ctx context.Context if req.Spec.Request == nil { m := &crd.NetHttpRequest{ - DurationInSecond: types.ControllerConfig.Configmap.NethttpDefaultRequestDurationInSecond, - QPS: types.ControllerConfig.Configmap.NethttpDefaultRequestQps, - PerRequestTimeoutInMS: types.ControllerConfig.Configmap.NethttpDefaultRequestPerRequestTimeoutInMS, + DurationInSecond: types.ControllerConfig.Configmap.NetHttpDefaultRequestDurationInSecond, + QPS: types.ControllerConfig.Configmap.NetHttpDefaultRequestQPS, + PerRequestTimeoutInMS: types.ControllerConfig.Configmap.NetHttpDefaultRequestPerRequestTimeoutInMS, } req.Spec.Request = m logger.Sugar().Debugf("set default Request for NetReach %v", req.Name) @@ -138,8 +138,8 @@ func (s *PluginNetReach) WebhookValidateCreate(logger *zap.Logger, ctx context.C // validate request if true { - if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps { - s := fmt.Sprintf("NetReach %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NethttpDefaultRequestMaxQps) + if r.Spec.Request.QPS >= types.ControllerConfig.Configmap.NetReachRequestMaxQPS { + s := fmt.Sprintf("NetReach %v requires qps %v bigger than maximum %v", r.Name, r.Spec.Request.QPS, types.ControllerConfig.Configmap.NetReachRequestMaxQPS) logger.Error(s) return apierrors.NewBadRequest(s) } diff --git a/pkg/types/types.go b/pkg/types/types.go index 07354ab2..2b454f1e 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -3,18 +3,28 @@ package types type ConfigmapConfig struct { - EnableIPv4 bool `yaml:"enableIPv4"` - EnableIPv6 bool `yaml:"enableIPv6"` - TaskPollIntervalInSecond int `yaml:"taskPollIntervalInSecond"` - NethttpDefaultRequestQps int `yaml:"nethttp_defaultRequest_Qps"` - NethttpDefaultRequestMaxQps int `yaml:"nethttp_defaultRequest_MaxQps"` - NethttpDefaultConcurrency int `yaml:"nethttp_defaultConcurrency"` - NethttpDefaultMaxIdleConnsPerHost int `yaml:"nethttp_defaultMaxIdleConnsPerHost"` - NethttpDefaultRequestDurationInSecond int `yaml:"nethttp_defaultRequest_DurationInSecond"` - NethttpDefaultRequestPerRequestTimeoutInMS int `yaml:"nethttp_defaultRequest_PerRequestTimeoutInMS"` - NetdnsDefaultConcurrency int `yaml:"netdns_defaultConcurrency"` - MultusPodAnnotationKey string `yaml:"multusPodAnnotationKey"` - CrdMaxHistory int `yaml:"crdMaxHistory"` + EnableIPv4 bool `yaml:"enableIPv4"` + EnableIPv6 bool `yaml:"enableIPv6"` + + TaskPollIntervalInSecond int `yaml:"taskPollIntervalInSecond"` + // nethttp + NetHttpDefaultRequestQPS int `yaml:"nethttpDefaultRequestQPS"` + NetHttpDefaultMaxIdleConnsPerHost int `yaml:"netHttpDefaultMaxIdleConnsPerHost"` + NetHttpDefaultRequestDurationInSecond int `yaml:"netHttpDefaultRequestDurationInSecond"` + NetHttpDefaultRequestPerRequestTimeoutInMS int `yaml:"netHttpDefaultRequestPerRequestTimeoutInMS"` + + // netreach + NetReachMaxConcurrency int `yaml:"netReachMaxConcurrency"` + NetReachRequestMaxQPS int `yaml:"netReachRequestMaxQPS"` + // apphttphealthy + AppHttpHealthyMaxConcurrency int `yaml:"appHttpHealthyMaxConcurrency"` + AppHttpHealthyRequestMaxQPS int `yaml:"appHttpHealthyRequestMaxQPS"` + // netdns + NetDnsMaxConcurrency int `yaml:"netDnsMaxConcurrency"` + NetDnsRequestMaxQPS int `yaml:"netDnsRequestMaxQPS"` + + MultusPodAnnotationKey string `yaml:"multusPodAnnotationKey"` + CrdMaxHistory int `yaml:"crdMaxHistory"` AgentSerivceIpv4Name string `yaml:"agentSerivceIpv4Name"` AgentSerivceIpv6Name string `yaml:"agentSerivceIpv6Name"` From 8bbc93f4ae900ad8c465a130f2eb1cb02cb0dbd3 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 18:10:49 +0800 Subject: [PATCH 31/41] fix configmap field error Signed-off-by: ii2day --- charts/README.md | 1 + charts/templates/configmap.yaml | 14 +++++++------- charts/values.yaml | 3 +++ pkg/types/types.go | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/charts/README.md b/charts/README.md index 464bab7e..5b973a95 100644 --- a/charts/README.md +++ b/charts/README.md @@ -29,6 +29,7 @@ | `feature.netReachMaxConcurrency` | concurrency for kind NetReach | `20` | | `feature.appHttpHealthyMaxConcurrency` | concurrency for kind AppHttpHealthy | `10` | | `feature.appHttpHealthyRequestMaxQPS` | qps for kind AppHttpHealthy | `100` | +| `feature.netHttpDefaultRequestQPS` | qps for kind NetHttp | `10` | | `feature.netHttpDefaultMaxIdleConnsPerHost` | max idle connect for kind NetHttp | `50` | | `feature.netHttpDefaultRequestDurationInSecond` | Duration In Second for kind NetHttp | `2` | | `feature.netHttpDefaultRequestPerRequestTimeoutInMS` | PerRequest Timeout In MS for kind NetHttp | `500` | diff --git a/charts/templates/configmap.yaml b/charts/templates/configmap.yaml index d0a492e3..d5af5d7b 100644 --- a/charts/templates/configmap.yaml +++ b/charts/templates/configmap.yaml @@ -16,13 +16,13 @@ data: enableIPv4: {{ .Values.feature.enableIPv4 }} enableIPv6: {{ .Values.feature.enableIPv6 }} taskPollIntervalInSecond: {{ .Values.feature.taskPollIntervalInSecond }} - nethttp_defaultRequest_Qps: {{ .Values.feature.nethttp_defaultRequest_Qps }} - nethttp_defaultRequest_MaxQps: {{ .Values.feature.nethttp_defaultRequest_MaxQps }} - nethttp_defaultConcurrency: {{ .Values.feature.nethttp_defaultConcurrency }} - nethttp_defaultMaxIdleConnsPerHost: {{ .Values.feature.nethttp_defaultMaxIdleConnsPerHost }} - nethttp_defaultRequest_DurationInSecond: {{ .Values.feature.nethttp_defaultRequest_DurationInSecond }} - nethttp_defaultRequest_PerRequestTimeoutInMS: {{ .Values.feature.nethttp_defaultRequest_PerRequestTimeoutInMS }} - netdns_defaultConcurrency: {{ .Values.feature.netdns_defaultConcurrency }} + netHttpDefaultRequestQPS: {{ .Values.feature.netHttpDefaultRequestQPS }} + netHttpDefaultMaxIdleConnsPerHost: {{ .Values.feature.netHttpDefaultMaxIdleConnsPerHost }} + netHttpDefaultRequestDurationInSecond: {{ .Values.feature.netHttpDefaultRequestDurationInSecond }} + netHttpDefaultRequestPerRequestTimeoutInMS: {{ .Values.feature.netHttpDefaultRequestPerRequestTimeoutInMS }} + netDnsMaxConcurrency: {{ .Values.feature.netDnsMaxConcurrency }} + netReachMaxConcurrency: {{ .Values.feature.netReachMaxConcurrency }} + appHttpHealthyMaxConcurrency: {{ .Values.feature.appHttpHealthyMaxConcurrency }} multusPodAnnotationKey: {{ .Values.feature.multusPodAnnotationKey }} agentDefaultTerminationGracePeriodMinutes: {{ .Values.feature.agentDefaultTerminationGracePeriodMinutes }} crdMaxHistory: {{ .Values.feature.crdMaxHistory }} diff --git a/charts/values.yaml b/charts/values.yaml index 3670b615..89889398 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -50,6 +50,9 @@ feature: ## @param feature.appHttpHealthyRequestMaxQPS qps for kind AppHttpHealthy appHttpHealthyRequestMaxQPS: 100 + ## @param feature.netHttpDefaultRequestQPS qps for kind NetHttp + netHttpDefaultRequestQPS: 10 + ## @param feature.netHttpDefaultMaxIdleConnsPerHost max idle connect for kind NetHttp netHttpDefaultMaxIdleConnsPerHost: 50 diff --git a/pkg/types/types.go b/pkg/types/types.go index 2b454f1e..23d3b0d6 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -8,7 +8,7 @@ type ConfigmapConfig struct { TaskPollIntervalInSecond int `yaml:"taskPollIntervalInSecond"` // nethttp - NetHttpDefaultRequestQPS int `yaml:"nethttpDefaultRequestQPS"` + NetHttpDefaultRequestQPS int `yaml:"netHttpDefaultRequestQPS"` NetHttpDefaultMaxIdleConnsPerHost int `yaml:"netHttpDefaultMaxIdleConnsPerHost"` NetHttpDefaultRequestDurationInSecond int `yaml:"netHttpDefaultRequestDurationInSecond"` NetHttpDefaultRequestPerRequestTimeoutInMS int `yaml:"netHttpDefaultRequestPerRequestTimeoutInMS"` From 433371eadd3409f0665ec30b86a9b3628d230631 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 18:47:42 +0800 Subject: [PATCH 32/41] Fix configmap missing fields Signed-off-by: ii2day --- charts/templates/configmap.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/templates/configmap.yaml b/charts/templates/configmap.yaml index d5af5d7b..2e6b19c7 100644 --- a/charts/templates/configmap.yaml +++ b/charts/templates/configmap.yaml @@ -21,8 +21,11 @@ data: netHttpDefaultRequestDurationInSecond: {{ .Values.feature.netHttpDefaultRequestDurationInSecond }} netHttpDefaultRequestPerRequestTimeoutInMS: {{ .Values.feature.netHttpDefaultRequestPerRequestTimeoutInMS }} netDnsMaxConcurrency: {{ .Values.feature.netDnsMaxConcurrency }} + netDnsRequestMaxQPS: {{ .Values.feature.netDnsRequestMaxQPS }} netReachMaxConcurrency: {{ .Values.feature.netReachMaxConcurrency }} + netReachRequestMaxQPS: {{ .Values.feature.netReachRequestMaxQPS }} appHttpHealthyMaxConcurrency: {{ .Values.feature.appHttpHealthyMaxConcurrency }} + appHttpHealthyRequestMaxQPS: {{ .Values.feature.appHttpHealthyRequestMaxQPS }} multusPodAnnotationKey: {{ .Values.feature.multusPodAnnotationKey }} agentDefaultTerminationGracePeriodMinutes: {{ .Values.feature.agentDefaultTerminationGracePeriodMinutes }} crdMaxHistory: {{ .Values.feature.crdMaxHistory }} From 16405bb01693168f1a3b0f2054ecbca8535a8bb4 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 19:05:13 +0800 Subject: [PATCH 33/41] Modify default qps limit university Signed-off-by: ii2day --- charts/README.md | 6 +++--- charts/values.yaml | 6 +++--- test/Makefile | 2 -- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/charts/README.md b/charts/README.md index 5b973a95..f5ef60ba 100644 --- a/charts/README.md +++ b/charts/README.md @@ -25,9 +25,9 @@ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------ | | `feature.enableIPv4` | enable ipv4 | `true` | | `feature.enableIPv6` | enable ipv6 | `true` | -| `feature.netReachRequestMaxQPS` | qps for kind NetReach | `10` | -| `feature.netReachMaxConcurrency` | concurrency for kind NetReach | `20` | -| `feature.appHttpHealthyMaxConcurrency` | concurrency for kind AppHttpHealthy | `10` | +| `feature.netReachRequestMaxQPS` | qps for kind NetReach | `20` | +| `feature.netReachMaxConcurrency` | concurrency for kind NetReach | `10` | +| `feature.appHttpHealthyMaxConcurrency` | concurrency for kind AppHttpHealthy | `20` | | `feature.appHttpHealthyRequestMaxQPS` | qps for kind AppHttpHealthy | `100` | | `feature.netHttpDefaultRequestQPS` | qps for kind NetHttp | `10` | | `feature.netHttpDefaultMaxIdleConnsPerHost` | max idle connect for kind NetHttp | `50` | diff --git a/charts/values.yaml b/charts/values.yaml index 89889398..10f34ce0 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -39,13 +39,13 @@ feature: enableIPv6: true ## @param feature.netReachRequestMaxQPS qps for kind NetReach - netReachRequestMaxQPS: 10 + netReachRequestMaxQPS: 20 ## @param feature.netReachMaxConcurrency concurrency for kind NetReach - netReachMaxConcurrency: 20 + netReachMaxConcurrency: 10 ## @param feature.appHttpHealthyMaxConcurrency concurrency for kind AppHttpHealthy - appHttpHealthyMaxConcurrency: 10 + appHttpHealthyMaxConcurrency: 20 ## @param feature.appHttpHealthyRequestMaxQPS qps for kind AppHttpHealthy appHttpHealthyRequestMaxQPS: 100 diff --git a/test/Makefile b/test/Makefile index 133cbf27..03ee1b2a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -159,8 +159,6 @@ deploy_project: HELM_OPTION+=" --set kdoctorAgent.ingress.enable=false " ; \ fi ; \ HELM_OPTION+=" --set feature.aggregateReport.enabled=true " ; \ - HELM_OPTION+=" --set feature.nethttp_defaultConcurrency=10 " ; \ - HELM_OPTION+=" --set feature.netdns_defaultConcurrency=10 " ; \ HELM_OPTION+=" --set kdoctorAgent.resources.requests.cpu=500m " ; \ HELM_OPTION+=" --set kdoctorAgent.resources.limits.cpu=2000m " ; \ HELM_OPTION+=" --set kdoctorAgent.resources.requests.memory=512Mi " ; \ From a97f3a46f0711ff16968887444fc15f177afa73f Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 19:52:10 +0800 Subject: [PATCH 34/41] update version to v0.2.1 Signed-off-by: ii2day --- VERSION | 2 +- charts/Chart.yaml | 4 ++-- test/chart-app/Chart.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 81fd7ba0..eac0a144 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.2.0 \ No newline at end of file +v0.2.1 \ No newline at end of file diff --git a/charts/Chart.yaml b/charts/Chart.yaml index 6cca4a55..6ac1cba4 100644 --- a/charts/Chart.yaml +++ b/charts/Chart.yaml @@ -5,11 +5,11 @@ icon: https://raw.githubusercontent.com/kdoctor-io/kdoctor/main/docs/images/kdoc # application or library type: application # no need to modify this version , CI will auto update it with /VERSION -version: 0.2.0 +version: 0.2.1 # This field is informational, and has no impact on chart version calculations . # Leaving it unquoted can lead to parsing issues in some cases # no need to modify this version , CI will auto update it with /VERSION -appVersion: "0.2.0" +appVersion: "0.2.1" kubeVersion: ">= 1.16.0-0" description: kdoctor sources: diff --git a/test/chart-app/Chart.yaml b/test/chart-app/Chart.yaml index 18829209..f51a541d 100644 --- a/test/chart-app/Chart.yaml +++ b/test/chart-app/Chart.yaml @@ -4,11 +4,11 @@ home: "https://kdoctor-io.github.io/kdoctor" # application or library type: application # no need to modify this version , CI will auto update it with /VERSION -version: 0.2.0 +version: 0.2.1 # This field is informational, and has no impact on chart version calculations . # Leaving it unquoted can lead to parsing issues in some cases # no need to modify this version , CI will auto update it with /VERSION -appVersion: "0.2.0" +appVersion: "0.2.1" kubeVersion: ">= 1.16.0-0" description: server for kdoctor test sources: From 994205b5ee898f6e1fd338b381e61261f4ed7681 Mon Sep 17 00:00:00 2001 From: ii2day <45119426+ii2day@users.noreply.github.com> Date: Sat, 30 Sep 2023 18:46:11 +0800 Subject: [PATCH 35/41] Merge pull request #233 from kdoctor-io/pr/ii2day/version update version to v0.2.0-rc1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index eac0a144..22c08f72 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.2.1 \ No newline at end of file +v0.2.1 From 5f2ac6b9a26c860c80291bb321f20814d6c56e89 Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 26 Sep 2023 16:37:10 +0800 Subject: [PATCH 36/41] fix netdns success rate err Signed-off-by: ii2day --- test/e2e/common/tools.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/e2e/common/tools.go b/test/e2e/common/tools.go index 7ab989a5..03e65ff4 100644 --- a/test/e2e/common/tools.go +++ b/test/e2e/common/tools.go @@ -254,6 +254,9 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") } + if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { + return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") + } } // startTime shcedule := pluginManager.NewSchedule(*rs.Spec.Schedule.Schedule) @@ -306,6 +309,9 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") } + if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { + return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") + } } // startTime shcedule := pluginManager.NewSchedule(*rs.Spec.Schedule.Schedule) @@ -371,6 +377,9 @@ func CompareResult(f *frame.Framework, name, taskKind string, podIPs []string, n if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") } + if float64(m.Metrics.SuccessCounts)/float64(m.Metrics.RequestCounts) != m.SucceedRate { + return GetResultFromReport(r), fmt.Errorf("succeedRate not equal") + } } // startTime shcedule := pluginManager.NewSchedule(*rs.Spec.Schedule.Schedule) From 6d288a6734095f6e17954787261032c84e1da575 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 27 Sep 2023 15:21:19 +0800 Subject: [PATCH 37/41] fix task timeout no report Signed-off-by: ii2day --- test/e2e/apphttphealth/apphttphealth_test.go | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 0ee37c9e..206222b8 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -878,4 +878,66 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) + + It("Failed http every round testing appHttpHealth due to delay ", Label("A00015"), func() { + var e error + successRate := float64(1) + successMean := int64(200) + crontab := "0 1" + appHttpHealthName := "apphttphealth-get" + tools.RandomName() + + appHttpHealth := new(v1beta1.AppHttpHealthy) + appHttpHealth.Name = appHttpHealthName + + // agentSpec + agentSpec := new(v1beta1.AgentSpec) + agentSpec.TerminationGracePeriodMinutes = &termMin + appHttpHealth.Spec.AgentSpec = *agentSpec + + // successCondition + successCondition := new(v1beta1.NetSuccessCondition) + successCondition.SuccessRate = &successRate + successCondition.MeanAccessDelayInMs = &successMean + appHttpHealth.Spec.SuccessCondition = successCondition + + // target + target := new(v1beta1.AppHttpHealthyTarget) + target.Method = "GET" + if net.ParseIP(testSvcIP).To4() == nil { + target.Host = fmt.Sprintf("http://[%s]:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) + } else { + target.Host = fmt.Sprintf("http://%s:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) + } + appHttpHealth.Spec.Target = target + + // request + request := new(v1beta1.NetHttpRequest) + request.PerRequestTimeoutInMS = 2000 + request.QPS = 10 + request.DurationInSecond = 60 + appHttpHealth.Spec.Request = request + + // Schedule + Schedule := new(v1beta1.SchedulePlan) + Schedule.Schedule = &crontab + Schedule.RoundNumber = 1 + Schedule.RoundTimeoutMinute = 1 + appHttpHealth.Spec.Schedule = Schedule + + e = frame.CreateResource(appHttpHealth) + Expect(e).NotTo(HaveOccurred(), "create appHttpHealth resource") + + e = common.CheckRuntime(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 60) + Expect(e).NotTo(HaveOccurred(), "check task runtime spec") + + e = common.WaitKdoctorTaskDone(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 120) + Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") + + success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum) + Expect(e).NotTo(HaveOccurred(), "compare report and task") + Expect(success).To(BeFalse(), "compare report and task result") + + e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) + Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") + }) }) From 9c51cbd6ff43124c9cd3a1b1f8b15f38cd32639d Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 13 Sep 2023 18:22:32 +0800 Subject: [PATCH 38/41] Fixed the netreach target configuration not taking effect Signed-off-by: ii2day --- test/e2e/apphttphealth/apphttphealth_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 206222b8..54183128 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -691,6 +691,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) +<<<<<<< HEAD Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") @@ -933,7 +934,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), e = common.WaitKdoctorTaskDone(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 120) Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") - success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum) + success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeFalse(), "compare report and task result") From 3a1179e3aae71fd925267b00408a2dfeb344ca0b Mon Sep 17 00:00:00 2001 From: weizhoublue <45163302+weizhoublue@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:28:58 +0800 Subject: [PATCH 39/41] Merge pull request #224 from kdoctor-io/pr/ii2day/e2e Increase the timeout time for e2e requests --- test/e2e/apphttphealth/apphttphealth_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 54183128..33f552a6 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -670,6 +670,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout + request.QPS = 10 request.DurationInSecond = 10 appHttpHealth.Spec.Request = request @@ -691,7 +692,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) -<<<<<<< HEAD + Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") @@ -852,6 +853,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout + request.QPS = 10 request.DurationInSecond = 10 appHttpHealth.Spec.Request = request @@ -913,7 +915,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // request request := new(v1beta1.NetHttpRequest) - request.PerRequestTimeoutInMS = 2000 + request.PerRequestTimeoutInMS = requestTimeout request.QPS = 10 request.DurationInSecond = 60 appHttpHealth.Spec.Request = request From a982d4c5f94d25918d3ad3b7a5b1d9d292ab043d Mon Sep 17 00:00:00 2001 From: ii2day Date: Tue, 31 Oct 2023 18:55:25 +0800 Subject: [PATCH 40/41] conflict Signed-off-by: ii2day --- test/e2e/apphttphealth/apphttphealth_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 33f552a6..7ec0a45f 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -768,6 +768,7 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) agentSpec.Kind = types.KindDeployment agentSpec.DeploymentReplicas = pointer.Int32(2) + agentSpec.TerminationGracePeriodMinutes = &termMin appHttpHealth.Spec.AgentSpec = agentSpec // successCondition From 39074a09c7718f38ea912b9888f48602d440adb3 Mon Sep 17 00:00:00 2001 From: ii2day Date: Wed, 29 Nov 2023 20:10:36 +0800 Subject: [PATCH 41/41] fix conflict Signed-off-by: ii2day --- test/e2e/apphttphealth/apphttphealth_test.go | 66 -------------------- 1 file changed, 66 deletions(-) diff --git a/test/e2e/apphttphealth/apphttphealth_test.go b/test/e2e/apphttphealth/apphttphealth_test.go index 7ec0a45f..0ee37c9e 100644 --- a/test/e2e/apphttphealth/apphttphealth_test.go +++ b/test/e2e/apphttphealth/apphttphealth_test.go @@ -670,7 +670,6 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 10 request.DurationInSecond = 10 appHttpHealth.Spec.Request = request @@ -692,7 +691,6 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) - Expect(e).NotTo(HaveOccurred(), "compare report and task") Expect(success).To(BeTrue(), "compare report and task result") @@ -768,7 +766,6 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), agentSpec.TerminationGracePeriodMinutes = pointer.Int64(1) agentSpec.Kind = types.KindDeployment agentSpec.DeploymentReplicas = pointer.Int32(2) - agentSpec.TerminationGracePeriodMinutes = &termMin appHttpHealth.Spec.AgentSpec = agentSpec // successCondition @@ -854,7 +851,6 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), // request request := new(v1beta1.NetHttpRequest) request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 10 request.DurationInSecond = 10 appHttpHealth.Spec.Request = request @@ -882,66 +878,4 @@ var _ = Describe("testing appHttpHealth test ", Serial, Label("appHttpHealth"), e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") }) - - It("Failed http every round testing appHttpHealth due to delay ", Label("A00015"), func() { - var e error - successRate := float64(1) - successMean := int64(200) - crontab := "0 1" - appHttpHealthName := "apphttphealth-get" + tools.RandomName() - - appHttpHealth := new(v1beta1.AppHttpHealthy) - appHttpHealth.Name = appHttpHealthName - - // agentSpec - agentSpec := new(v1beta1.AgentSpec) - agentSpec.TerminationGracePeriodMinutes = &termMin - appHttpHealth.Spec.AgentSpec = *agentSpec - - // successCondition - successCondition := new(v1beta1.NetSuccessCondition) - successCondition.SuccessRate = &successRate - successCondition.MeanAccessDelayInMs = &successMean - appHttpHealth.Spec.SuccessCondition = successCondition - - // target - target := new(v1beta1.AppHttpHealthyTarget) - target.Method = "GET" - if net.ParseIP(testSvcIP).To4() == nil { - target.Host = fmt.Sprintf("http://[%s]:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) - } else { - target.Host = fmt.Sprintf("http://%s:%d/?delay=1&task=%s", testSvcIP, httpPort, appHttpHealthName) - } - appHttpHealth.Spec.Target = target - - // request - request := new(v1beta1.NetHttpRequest) - request.PerRequestTimeoutInMS = requestTimeout - request.QPS = 10 - request.DurationInSecond = 60 - appHttpHealth.Spec.Request = request - - // Schedule - Schedule := new(v1beta1.SchedulePlan) - Schedule.Schedule = &crontab - Schedule.RoundNumber = 1 - Schedule.RoundTimeoutMinute = 1 - appHttpHealth.Spec.Schedule = Schedule - - e = frame.CreateResource(appHttpHealth) - Expect(e).NotTo(HaveOccurred(), "create appHttpHealth resource") - - e = common.CheckRuntime(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 60) - Expect(e).NotTo(HaveOccurred(), "check task runtime spec") - - e = common.WaitKdoctorTaskDone(frame, appHttpHealth, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "wait appHttpHealth task finish") - - success, e := common.CompareResult(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, testPodIPs, reportNum, appHttpHealth) - Expect(e).NotTo(HaveOccurred(), "compare report and task") - Expect(success).To(BeFalse(), "compare report and task result") - - e = common.CheckRuntimeDeadLine(frame, appHttpHealthName, pluginManager.KindNameAppHttpHealthy, 120) - Expect(e).NotTo(HaveOccurred(), "check task runtime resource delete") - }) })