diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 75696ffde..cbab249e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x - name: Check out code into the Go module directory uses: actions/checkout@v2 @@ -32,10 +32,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v2 with: - go-version: 1.20.x + go-version: 1.21.x - name: Check out code into the Go module directory uses: actions/checkout@v2 @@ -46,6 +46,9 @@ jobs: - name: test controllers on opensfhit run: CLUSTER_TYPE=openshift make test-controllers + - name: test controllers on kubernetes + run: CLUSTER_TYPE=kubernetes make test-controllers + - name: test bindata/scripts run: make test-bindata-scripts @@ -53,27 +56,27 @@ jobs: name: Golangci-lint runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v2 with: - go-version: 1.20.x + go-version: 1.21.x - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.51.0 + version: v1.55.2 test-coverage: name: test-coverage runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v2 with: - go-version: 1.20.x + go-version: 1.21.x - name: Check out code into the Go module directory uses: actions/checkout@v2 @@ -107,10 +110,10 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x - name: run test run: make test-e2e-conformance-virtual-k8s-cluster-ci @@ -141,10 +144,10 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 - - name: Set up Go 1.20 + - name: Set up Go 1.21 uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x - name: run test run: make test-e2e-conformance-virtual-ocp-cluster-ci diff --git a/.gitignore b/.gitignore index e63284190..6761d7ea1 100644 --- a/.gitignore +++ b/.gitignore @@ -82,4 +82,5 @@ tags # End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode #IDE (GoLand) specific .idea/ - +# test-environment files +registry-login.conf diff --git a/.golangci.yml b/.golangci.yml index d21d5c8c4..253c4ff23 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,13 +10,11 @@ run: - pkg/client linters-settings: depguard: - list-type: blacklist - packages: - # logging is allowed only by logutils.Log, logrus - # is allowed to use only in logutils package - - github.com/sirupsen/logrus - packages-with-error-message: - - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + rules: + main: + deny: + - pkg: github.com/sirupsen/logrus + desc: "logging is allowed only by logutils.Log" dupl: threshold: 100 funlen: @@ -25,6 +23,7 @@ linters-settings: goconst: min-len: 2 min-occurrences: 2 + ignore-tests: true gocritic: enabled-tags: - diagnostic @@ -118,4 +117,7 @@ issues: - lll - stylecheck - goconst + - path: test/conformance/tests + linters: + - goconst diff --git a/Dockerfile b/Dockerfile index 7e871754c..8e503f2ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20 AS builder +FROM golang:1.21 AS builder WORKDIR /go/src/github.com/k8snetworkplumbingwg/sriov-network-operator COPY . . RUN make _build-manager BIN_PATH=build/_output/cmd diff --git a/Dockerfile.sriov-network-config-daemon b/Dockerfile.sriov-network-config-daemon index e37ea87b9..6f7069362 100644 --- a/Dockerfile.sriov-network-config-daemon +++ b/Dockerfile.sriov-network-config-daemon @@ -1,4 +1,4 @@ -FROM golang:1.20 AS builder +FROM golang:1.21 AS builder WORKDIR /go/src/github.com/k8snetworkplumbingwg/sriov-network-operator COPY . . RUN make _build-sriov-network-config-daemon BIN_PATH=build/_output/cmd diff --git a/Dockerfile.webhook b/Dockerfile.webhook index bc3994917..2ea2c006d 100644 --- a/Dockerfile.webhook +++ b/Dockerfile.webhook @@ -1,4 +1,4 @@ -FROM golang:1.20 AS builder +FROM golang:1.21 AS builder WORKDIR /go/src/github.com/k8snetworkplumbingwg/sriov-network-operator COPY . . RUN make _build-webhook BIN_PATH=build/_output/cmd diff --git a/Makefile b/Makefile index 6022c54b9..09681353b 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ export WATCH_NAMESPACE?=openshift-sriov-network-operator export GOFLAGS+=-mod=vendor export GO111MODULE=on PKGS=$(shell go list ./... | grep -v -E '/vendor/|/test|/examples') +TESTPKGS?=./... # go source files, ignore vendor directory SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*") @@ -51,7 +52,7 @@ GOLANGCI_LINT = $(BIN_DIR)/golangci-lint # golangci-lint version should be updated periodically # we keep it fixed to avoid it from unexpectedly failing on the project # in case of a version bump -GOLANGCI_LINT_VER = v1.51.0 +GOLANGCI_LINT_VER = v1.55.2 GOLANGCI_LINT = $(BIN_DIR)/golangci-lint # golangci-lint version should be updated periodically @@ -62,7 +63,7 @@ GOLANGCI_LINT_VER = v1.46.1 .PHONY: all build clean gendeepcopy test test-e2e test-e2e-k8s run image fmt sync-manifests test-e2e-conformance manifests update-codegen -all: generate vet build +all: generate lint build build: manager _build-sriov-network-config-daemon _build-webhook @@ -82,14 +83,14 @@ image: ; $(info Building images...) $(IMAGE_BUILDER) build -f $(DOCKERFILE_WEBHOOK) -t $(WEBHOOK_IMAGE_TAG) $(CURPATH) $(IMAGE_BUILD_OPTS) # Run tests -test: generate vet manifests envtest - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir=/tmp -p path)" HOME="$(shell pwd)" go test ./... -coverprofile cover.out -v +test: generate lint manifests envtest + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir=/tmp -p path)" HOME="$(shell pwd)" go test -coverprofile cover.out -v ${TESTPKGS} # Build manager binary -manager: generate vet _build-manager +manager: generate _build-manager # Run against the configured Kubernetes cluster in ~/.kube/config -run: vet skopeo install +run: skopeo install hack/run-locally.sh # Install CRDs into a cluster @@ -137,10 +138,6 @@ fmt: ## Go fmt your code fmt-code: go fmt ./... -# Run go vet against code -vet: - go vet ./... - # Generate code generate: controller-gen $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." @@ -162,7 +159,7 @@ envtest: ## Download envtest-setup locally if necessary. GOMOCK = $(shell pwd)/bin/mockgen gomock: - $(call go-get-tool,$(GOMOCK),github.com/golang/mock/mockgen@v1.6.0) + $(call go-install-tool,$(GOMOCK),github.com/golang/mock/mockgen@v1.6.0) # go-install-tool will 'go install' any package $2 and install it to $1. define go-install-tool @@ -210,7 +207,7 @@ redeploy-operator-virtual-cluster: test-e2e-validation-only: SUITE=./test/validation ./hack/run-e2e-conformance.sh -test-e2e: generate vet manifests skopeo envtest +test-e2e: generate manifests skopeo envtest KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir=/tmp -p path)"; source hack/env.sh; HOME="$(shell pwd)" go test ./test/e2e/... -timeout 60m -coverprofile cover.out -v test-e2e-k8s: export NAMESPACE=sriov-network-operator @@ -219,14 +216,9 @@ test-e2e-k8s: test-e2e test-bindata-scripts: fakechroot fakechroot ./test/scripts/enable-kargs_test.sh -test-%: generate vet manifests envtest +test-%: generate manifests envtest KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir=/tmp -p path)" HOME="$(shell pwd)" go test ./$*/... -coverprofile cover-$*.out -coverpkg ./... -v -# deploy-setup-k8s: export NAMESPACE=sriov-network-operator -# deploy-setup-k8s: export ADMISSION_CONTROLLERS_ENABLED=false -# deploy-setup-k8s: export CNI_BIN_PATH=/opt/cni/bin -# test-e2e-k8s: test-e2e - GOCOVMERGE = $(BIN_DIR)/gocovmerge gocovmerge: ## Download gocovmerge locally if necessary. $(call go-install-tool,$(GOCOVMERGE),github.com/shabbyrobe/gocovmerge/cmd/gocovmerge@latest) diff --git a/api/v1/helper.go b/api/v1/helper.go index ba2429caf..712a54874 100644 --- a/api/v1/helper.go +++ b/api/v1/helper.go @@ -20,7 +20,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) const ( @@ -35,7 +37,8 @@ const ( SriovCniStateAuto = "auto" SriovCniStateOff = "off" SriovCniStateOn = "on" - SriovCniIpamEmpty = "\"ipam\":{}" + SriovCniIpam = "\"ipam\"" + SriovCniIpamEmpty = SriovCniIpam + ":{}" ) const invalidVfIndex = -1 @@ -47,6 +50,8 @@ var log = logf.Log.WithName("sriovnetwork") // Vendor ID, Physical Function Device ID, Virtual Function Device ID var NicIDMap = []string{} +var InitialState SriovNetworkNodeState + // NetFilterType Represents the NetFilter tags to be used type NetFilterType int @@ -211,6 +216,80 @@ func GetVfDeviceID(deviceID string) string { return "" } +func IsSwitchdevModeSpec(spec SriovNetworkNodeStateSpec) bool { + for _, iface := range spec.Interfaces { + if iface.EswitchMode == ESwithModeSwitchDev { + return true + } + } + return false +} + +func FindInterface(interfaces Interfaces, name string) (iface Interface, err error) { + for _, i := range interfaces { + if i.Name == name { + return i, nil + } + } + return Interface{}, fmt.Errorf("unable to find interface: %v", name) +} + +func NeedToUpdateSriov(ifaceSpec *Interface, ifaceStatus *InterfaceExt) bool { + if ifaceSpec.Mtu > 0 { + mtu := ifaceSpec.Mtu + if mtu != ifaceStatus.Mtu { + log.V(2).Info("NeedToUpdateSriov(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu) + return true + } + } + + if ifaceSpec.NumVfs != ifaceStatus.NumVfs { + log.V(2).Info("NeedToUpdateSriov(): NumVfs needs update", "desired", ifaceSpec.NumVfs, "current", ifaceStatus.NumVfs) + return true + } + if ifaceSpec.NumVfs > 0 { + for _, vfStatus := range ifaceStatus.VFs { + ingroup := false + for _, groupSpec := range ifaceSpec.VfGroups { + if IndexInRange(vfStatus.VfID, groupSpec.VfRange) { + ingroup = true + if groupSpec.DeviceType != consts.DeviceTypeNetDevice { + if groupSpec.DeviceType != vfStatus.Driver { + log.V(2).Info("NeedToUpdateSriov(): Driver needs update", + "desired", groupSpec.DeviceType, "current", vfStatus.Driver) + return true + } + } else { + if StringInArray(vfStatus.Driver, vars.DpdkDrivers) { + log.V(2).Info("NeedToUpdateSriov(): Driver needs update", + "desired", groupSpec.DeviceType, "current", vfStatus.Driver) + return true + } + if vfStatus.Mtu != 0 && groupSpec.Mtu != 0 && vfStatus.Mtu != groupSpec.Mtu { + log.V(2).Info("NeedToUpdateSriov(): VF MTU needs update", + "vf", vfStatus.VfID, "desired", groupSpec.Mtu, "current", vfStatus.Mtu) + return true + } + + // this is needed to be sure the admin mac address is configured as expected + if ifaceSpec.ExternallyManaged { + log.V(2).Info("NeedToUpdateSriov(): need to update the device as it's externally manage", + "device", ifaceStatus.PciAddress) + return true + } + } + break + } + } + if !ingroup && StringInArray(vfStatus.Driver, vars.DpdkDrivers) { + // VF which has DPDK driver loaded but not in any group, needs to be reset to default driver. + return true + } + } + } + return false +} + type ByPriority []SriovNetworkNodePolicy func (a ByPriority) Len() int { @@ -517,7 +596,7 @@ func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) { } if cr.Spec.IPAM != "" { - data.Data["SriovCniIpam"] = "\"ipam\":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") + data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") } else { data.Data["SriovCniIpam"] = SriovCniIpamEmpty } @@ -652,7 +731,7 @@ func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) { } if cr.Spec.IPAM != "" { - data.Data["SriovCniIpam"] = "\"ipam\":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") + data.Data["SriovCniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "") } else { data.Data["SriovCniIpam"] = SriovCniIpamEmpty } diff --git a/api/v1/sriovnetworknodestate_types.go b/api/v1/sriovnetworknodestate_types.go index a653b391f..8d4023adb 100644 --- a/api/v1/sriovnetworknodestate_types.go +++ b/api/v1/sriovnetworknodestate_types.go @@ -82,6 +82,7 @@ type VirtualFunction struct { Vlan int `json:"Vlan,omitempty"` Mtu int `json:"mtu,omitempty"` VfID int `json:"vfID"` + VdpaType string `json:"vdpaType,omitempty"` } // SriovNetworkNodeStateStatus defines the observed state of SriovNetworkNodeState diff --git a/cmd/sriov-network-config-daemon/service.go b/cmd/sriov-network-config-daemon/service.go index fc9cfc6d9..3305d7e8e 100644 --- a/cmd/sriov-network-config-daemon/service.go +++ b/cmd/sriov-network-config-daemon/service.go @@ -24,15 +24,16 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" + snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/generic" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/virtual" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/systemd" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/version" - - snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" ) var ( @@ -57,11 +58,16 @@ func runServiceCmd(cmd *cobra.Command, args []string) error { setupLog.V(2).Info("sriov-config-service", "version", version.Version) setupLog.V(0).Info("Starting sriov-config-service") + + // Mark that we are running on host + vars.UsingSystemdMode = true + vars.InChroot = true + supportedNicIds, err := systemd.ReadSriovSupportedNics() if err != nil { setupLog.Error(err, "failed to read list of supported nic ids") sriovResult := &systemd.SriovResult{ - SyncStatus: "Failed", + SyncStatus: consts.SyncStatusFailed, LastSyncError: fmt.Sprintf("failed to read list of supported nic ids: %v", err), } err = systemd.WriteSriovResult(sriovResult) @@ -78,7 +84,7 @@ func runServiceCmd(cmd *cobra.Command, args []string) error { if _, err := os.Stat(systemd.SriovSystemdConfigPath); !errors.Is(err, os.ErrNotExist) { setupLog.Error(err, "failed to read the sriov configuration file", "path", systemd.SriovSystemdConfigPath) sriovResult := &systemd.SriovResult{ - SyncStatus: "Failed", + SyncStatus: consts.SyncStatusFailed, LastSyncError: fmt.Sprintf("failed to read the sriov configuration file in path %s: %v", systemd.SriovSystemdConfigPath, err), } err = systemd.WriteSriovResult(sriovResult) @@ -91,67 +97,64 @@ func runServiceCmd(cmd *cobra.Command, args []string) error { nodeStateSpec = &systemd.SriovConfig{ Spec: sriovv1.SriovNetworkNodeStateSpec{}, UnsupportedNics: false, - PlatformType: utils.Baremetal, + PlatformType: consts.Baremetal, } } setupLog.V(2).Info("sriov-config-service", "config", nodeStateSpec) - - storeManager, err := utils.NewStoreManager(true) + hostHelpers, err := helper.NewDefaultHostHelpers() if err != nil { - setupLog.Error(err, "failed to create store manager") - return err + setupLog.Error(err, "failed to create hostHelpers") + return updateSriovResultErr("failed to create hostHelpers") } - // Load kernel modules - hostManager := host.NewHostManager(true) - _, err = hostManager.TryEnableRdma() + platformHelper, err := platforms.NewDefaultPlatformHelper() + if err != nil { + setupLog.Error(err, "failed to create platformHelpers") + return updateSriovResultErr("failed to create platformHelpers") + } + + _, err = hostHelpers.TryEnableRdma() if err != nil { setupLog.Error(err, "warning, failed to enable RDMA") } - hostManager.TryEnableTun() - hostManager.TryEnableVhostNet() + hostHelpers.TryEnableTun() + hostHelpers.TryEnableVhostNet() var configPlugin plugin.VendorPlugin var ifaceStatuses []sriovv1.InterfaceExt - if nodeStateSpec.PlatformType == utils.Baremetal { + if nodeStateSpec.PlatformType == consts.Baremetal { // Bare metal support - ifaceStatuses, err = utils.DiscoverSriovDevices(nodeStateSpec.UnsupportedNics, storeManager) + vars.DevMode = nodeStateSpec.UnsupportedNics + ifaceStatuses, err = hostHelpers.DiscoverSriovDevices(hostHelpers) if err != nil { setupLog.Error(err, "failed to discover sriov devices on the host") - return fmt.Errorf("sriov-config-service: failed to discover sriov devices on the host: %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service: failed to discover sriov devices on the host: %v", err)) } // Create the generic plugin - configPlugin, err = generic.NewGenericPlugin(true, hostManager, storeManager) + configPlugin, err = generic.NewGenericPlugin(hostHelpers) if err != nil { setupLog.Error(err, "failed to create generic plugin") - return fmt.Errorf("sriov-config-service failed to create generic plugin %v", err) - } - } else if nodeStateSpec.PlatformType == utils.VirtualOpenStack { - // Openstack support - metaData, networkData, err := utils.GetOpenstackData(false) - if err != nil { - setupLog.Error(err, "failed to read OpenStack data") - return fmt.Errorf("sriov-config-service failed to read OpenStack data: %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service failed to create generic plugin %v", err)) } - - openStackDevicesInfo, err := utils.CreateOpenstackDevicesInfo(metaData, networkData) + } else if nodeStateSpec.PlatformType == consts.VirtualOpenStack { + err = platformHelper.CreateOpenstackDevicesInfo() if err != nil { setupLog.Error(err, "failed to read OpenStack data") - return fmt.Errorf("sriov-config-service failed to read OpenStack data: %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service failed to read OpenStack data: %v", err)) } - ifaceStatuses, err = utils.DiscoverSriovDevicesVirtual(openStackDevicesInfo) + ifaceStatuses, err = platformHelper.DiscoverSriovDevicesVirtual() if err != nil { setupLog.Error(err, "failed to read OpenStack data") - return fmt.Errorf("sriov-config-service: failed to read OpenStack data: %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service: failed to read OpenStack data: %v", err)) } // Create the virtual plugin - configPlugin, err = virtual.NewVirtualPlugin(true) + configPlugin, err = virtual.NewVirtualPlugin(hostHelpers) if err != nil { setupLog.Error(err, "failed to create virtual plugin") - return fmt.Errorf("sriov-config-service: failed to create virtual plugin %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service: failed to create virtual plugin %v", err)) } } @@ -163,18 +166,18 @@ func runServiceCmd(cmd *cobra.Command, args []string) error { _, _, err = configPlugin.OnNodeStateChange(nodeState) if err != nil { setupLog.Error(err, "failed to run OnNodeStateChange to update the generic plugin status") - return fmt.Errorf("sriov-config-service: failed to run OnNodeStateChange to update the generic plugin status %v", err) + return updateSriovResultErr(fmt.Sprintf("sriov-config-service: failed to run OnNodeStateChange to update the generic plugin status %v", err)) } sriovResult := &systemd.SriovResult{ - SyncStatus: "Succeeded", + SyncStatus: consts.SyncStatusSucceeded, LastSyncError: "", } err = configPlugin.Apply() if err != nil { setupLog.Error(err, "failed to run apply node configuration") - sriovResult.SyncStatus = "Failed" + sriovResult.SyncStatus = consts.SyncStatusFailed sriovResult.LastSyncError = err.Error() } @@ -187,3 +190,17 @@ func runServiceCmd(cmd *cobra.Command, args []string) error { setupLog.V(0).Info("shutting down sriov-config-service") return nil } + +func updateSriovResultErr(errMsg string) error { + sriovResult := &systemd.SriovResult{ + SyncStatus: consts.SyncStatusFailed, + LastSyncError: errMsg, + } + + err := systemd.WriteSriovResult(sriovResult) + if err != nil { + log.Log.Error(err, "failed to write sriov result file", "content", *sriovResult) + return fmt.Errorf("sriov-config-service failed to write sriov result file with content %v error: %v", *sriovResult, err) + } + return nil +} diff --git a/cmd/sriov-network-config-daemon/start.go b/cmd/sriov-network-config-daemon/start.go index a7feffbde..1d7d4d00c 100644 --- a/cmd/sriov-network-config-daemon/start.go +++ b/cmd/sriov-network-config-daemon/start.go @@ -24,8 +24,6 @@ import ( "strings" "time" - configv1 "github.com/openshift/api/config/v1" - mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -35,12 +33,17 @@ import ( "k8s.io/client-go/util/connrotation" "sigs.k8s.io/controller-runtime/pkg/log" + configv1 "github.com/openshift/api/config/v1" + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/daemon" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/version" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) var ( @@ -70,8 +73,11 @@ func runStartCmd(cmd *cobra.Command, args []string) error { snolog.InitLog() setupLog := log.Log.WithName("sriov-network-config-daemon") - // To help debugging, immediately log version - setupLog.V(2).Info("sriov-network-config-daemon", "version", version.Version) + // Mark that we are running inside a container + vars.UsingSystemdMode = false + if startOpts.systemd { + vars.UsingSystemdMode = true + } if startOpts.nodeName == "" { name, ok := os.LookupEnv("NODE_NAME") @@ -80,6 +86,7 @@ func runStartCmd(cmd *cobra.Command, args []string) error { } startOpts.nodeName = name } + vars.NodeName = startOpts.nodeName // This channel is used to ensure all spawned goroutines exit when we exit. stopCh := make(chan struct{}) @@ -102,7 +109,9 @@ func runStartCmd(cmd *cobra.Command, args []string) error { var config *rest.Config var err error - if os.Getenv("CLUSTER_TYPE") == utils.ClusterTypeOpenshift { + // On openshift we use the kubeconfig from kubelet on the node where the daemon is running + // this allow us to improve security as every daemon has access only to its own node + if vars.ClusterType == consts.ClusterTypeOpenshift { kubeconfig, err := clientcmd.LoadFromFile("/host/etc/kubernetes/kubeconfig") if err != nil { setupLog.Error(err, "failed to load kubelet kubeconfig") @@ -110,7 +119,7 @@ func runStartCmd(cmd *cobra.Command, args []string) error { clusterName := kubeconfig.Contexts[kubeconfig.CurrentContext].Cluster apiURL := kubeconfig.Clusters[clusterName].Server - url, err := url.Parse(apiURL) + urlPath, err := url.Parse(apiURL) if err != nil { setupLog.Error(err, "failed to parse api url from kubelet kubeconfig") } @@ -118,8 +127,14 @@ func runStartCmd(cmd *cobra.Command, args []string) error { // The kubernetes in-cluster functions don't let you override the apiserver // directly; gotta "pass" it via environment vars. setupLog.V(0).Info("overriding kubernetes api", "new-url", apiURL) - os.Setenv("KUBERNETES_SERVICE_HOST", url.Hostname()) - os.Setenv("KUBERNETES_SERVICE_PORT", url.Port()) + err = os.Setenv("KUBERNETES_SERVICE_HOST", urlPath.Hostname()) + if err != nil { + setupLog.Error(err, "failed to set KUBERNETES_SERVICE_HOST environment variable") + } + err = os.Setenv("KUBERNETES_SERVICE_PORT", urlPath.Port()) + if err != nil { + setupLog.Error(err, "failed to set KUBERNETES_SERVICE_PORT environment variable") + } } kubeconfig := os.Getenv("KUBECONFIG") @@ -134,57 +149,72 @@ func runStartCmd(cmd *cobra.Command, args []string) error { return err } + vars.Config = config + vars.Scheme = scheme.Scheme + closeAllConns, err := updateDialer(config) if err != nil { return err } - sriovnetworkv1.AddToScheme(scheme.Scheme) - mcfgv1.AddToScheme(scheme.Scheme) - configv1.Install(scheme.Scheme) + err = sriovnetworkv1.AddToScheme(scheme.Scheme) + if err != nil { + setupLog.Error(err, "failed to load sriov network CRDs to scheme") + return err + } + + err = mcfgv1.AddToScheme(scheme.Scheme) + if err != nil { + setupLog.Error(err, "failed to load machine config CRDs to scheme") + return err + } + + err = configv1.Install(scheme.Scheme) + if err != nil { + setupLog.Error(err, "failed to load openshift config CRDs to scheme") + return err + } snclient := snclientset.NewForConfigOrDie(config) kubeclient := kubernetes.NewForConfigOrDie(config) - openshiftContext, err := utils.NewOpenshiftContext(config, scheme.Scheme) + + hostHelpers, err := helper.NewDefaultHostHelpers() + if err != nil { + setupLog.Error(err, "failed to create hostHelpers") + return err + } + + platformHelper, err := platforms.NewDefaultPlatformHelper() if err != nil { + setupLog.Error(err, "failed to create platformHelper") return err } config.Timeout = 5 * time.Second writerclient := snclientset.NewForConfigOrDie(config) - mode := os.Getenv("DEV_MODE") - devMode := false - if mode == "TRUE" { - devMode = true - setupLog.V(0).Info("dev mode enabled") - } - - eventRecorder := daemon.NewEventRecorder(writerclient, startOpts.nodeName, kubeclient) + eventRecorder := daemon.NewEventRecorder(writerclient, kubeclient) defer eventRecorder.Shutdown() setupLog.V(0).Info("starting node writer") - nodeWriter := daemon.NewNodeStateStatusWriter(writerclient, startOpts.nodeName, closeAllConns, eventRecorder, devMode) - - destdir := os.Getenv("DEST_DIR") - if destdir == "" { - destdir = "/host/tmp" - } - - platformType := utils.Baremetal + nodeWriter := daemon.NewNodeStateStatusWriter(writerclient, + closeAllConns, + eventRecorder, + hostHelpers, + platformHelper) nodeInfo, err := kubeclient.CoreV1().Nodes().Get(context.Background(), startOpts.nodeName, v1.GetOptions{}) if err == nil { - for key, pType := range utils.PlatformMap { + for key, pType := range vars.PlatformsMap { if strings.Contains(strings.ToLower(nodeInfo.Spec.ProviderID), strings.ToLower(key)) { - platformType = pType + vars.PlatformType = pType } } } else { setupLog.Error(err, "failed to fetch node state, exiting", "node-name", startOpts.nodeName) return err } - setupLog.Info("Running on", "platform", platformType.String()) + setupLog.Info("Running on", "platform", vars.PlatformType.String()) var namespace = os.Getenv("NAMESPACE") if err := sriovnetworkv1.InitNicIDMapFromConfigMap(kubeclient, namespace); err != nil { @@ -195,27 +225,24 @@ func runStartCmd(cmd *cobra.Command, args []string) error { eventRecorder.SendEvent("ConfigDaemonStart", "Config Daemon starting") // block the deamon process until nodeWriter finish first its run - err = nodeWriter.RunOnce(destdir, platformType) + err = nodeWriter.RunOnce() if err != nil { setupLog.Error(err, "failed to run writer") return err } - go nodeWriter.Run(stopCh, refreshCh, syncCh, platformType) + go nodeWriter.Run(stopCh, refreshCh, syncCh) setupLog.V(0).Info("Starting SriovNetworkConfigDaemon") err = daemon.New( - startOpts.nodeName, snclient, kubeclient, - openshiftContext, + hostHelpers, + platformHelper, exitCh, stopCh, syncCh, refreshCh, - platformType, - startOpts.systemd, eventRecorder, - devMode, ).Run(stopCh, exitCh) if err != nil { setupLog.Error(err, "failed to run daemon") diff --git a/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodestates.yaml b/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodestates.yaml index 8e756f681..e5674e3df 100644 --- a/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodestates.yaml +++ b/config/crd/bases/sriovnetwork.openshift.io_sriovnetworknodestates.yaml @@ -112,6 +112,8 @@ spec: type: string pciAddress: type: string + vdpaType: + type: string vendor: type: string vfID: diff --git a/controllers/helper.go b/controllers/helper.go index f36ce44bc..f0380f671 100644 --- a/controllers/helper.go +++ b/controllers/helper.go @@ -18,11 +18,28 @@ package controllers import ( "bytes" + "context" "encoding/json" + "fmt" "os" "strings" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + kscheme "k8s.io/client-go/kubernetes/scheme" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) var webhooks = map[string](string){ @@ -39,8 +56,6 @@ const ( trueString = "true" ) -var namespace = os.Getenv("NAMESPACE") - func GetImagePullSecrets() []string { imagePullSecrets := os.Getenv("IMAGE_PULL_SECRETS") if imagePullSecrets != "" { @@ -62,3 +77,156 @@ func GetDefaultNodeSelector() map[string]string { return map[string]string{"node-role.kubernetes.io/worker": "", "kubernetes.io/os": "linux"} } + +func syncPluginDaemonObjs(ctx context.Context, client k8sclient.Client, scheme *runtime.Scheme, dp *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList) error { + logger := log.Log.WithName("syncPluginDaemonObjs") + logger.V(1).Info("Start to sync sriov daemons objects") + + // render plugin manifests + data := render.MakeRenderData() + data.Data["Namespace"] = vars.Namespace + data.Data["SRIOVDevicePluginImage"] = os.Getenv("SRIOV_DEVICE_PLUGIN_IMAGE") + data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION") + data.Data["ResourcePrefix"] = os.Getenv("RESOURCE_PREFIX") + data.Data["ImagePullSecrets"] = GetImagePullSecrets() + data.Data["NodeSelectorField"] = GetDefaultNodeSelector() + + defaultConfig := &sriovnetworkv1.SriovOperatorConfig{} + err := client.Get(ctx, types.NamespacedName{ + Name: constants.DefaultConfigName, Namespace: vars.Namespace}, defaultConfig) + if err != nil { + return err + } + data.Data["UseCDI"] = defaultConfig.Spec.UseCDI + objs, err := renderDsForCR(constants.PluginPath, &data) + if err != nil { + logger.Error(err, "Fail to render SR-IoV manifests") + return err + } + + if len(pl.Items) < 2 { + for _, obj := range objs { + err := deleteK8sResource(ctx, client, obj) + if err != nil { + return err + } + } + return nil + } + + // Sync DaemonSets + for _, obj := range objs { + if obj.GetKind() == constants.DaemonSet && len(defaultConfig.Spec.ConfigDaemonNodeSelector) > 0 { + scheme := kscheme.Scheme + ds := &appsv1.DaemonSet{} + err = scheme.Convert(obj, ds, nil) + if err != nil { + logger.Error(err, "Fail to convert to DaemonSet") + return err + } + ds.Spec.Template.Spec.NodeSelector = defaultConfig.Spec.ConfigDaemonNodeSelector + err = scheme.Convert(ds, obj, nil) + if err != nil { + logger.Error(err, "Fail to convert to Unstructured") + return err + } + } + err = syncDsObject(ctx, client, scheme, dp, pl, obj) + if err != nil { + logger.Error(err, "Couldn't sync SR-IoV daemons objects") + return err + } + } + + return nil +} + +func deleteK8sResource(ctx context.Context, client k8sclient.Client, in *uns.Unstructured) error { + if err := apply.DeleteObject(ctx, client, in); err != nil { + return fmt.Errorf("failed to delete object %v with err: %v", in, err) + } + return nil +} + +func syncDsObject(ctx context.Context, client k8sclient.Client, scheme *runtime.Scheme, dp *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList, obj *uns.Unstructured) error { + logger := log.Log.WithName("syncDsObject") + kind := obj.GetKind() + logger.V(1).Info("Start to sync Objects", "Kind", kind) + switch kind { + case constants.ServiceAccount, constants.Role, constants.RoleBinding: + if err := controllerutil.SetControllerReference(dp, obj, scheme); err != nil { + return err + } + if err := apply.ApplyObject(ctx, client, obj); err != nil { + logger.Error(err, "Fail to sync", "Kind", kind) + return err + } + case constants.DaemonSet: + ds := &appsv1.DaemonSet{} + err := scheme.Convert(obj, ds, nil) + if err != nil { + logger.Error(err, "Fail to convert to DaemonSet") + return err + } + err = syncDaemonSet(ctx, client, scheme, dp, pl, ds) + if err != nil { + logger.Error(err, "Fail to sync DaemonSet", "Namespace", ds.Namespace, "Name", ds.Name) + return err + } + } + return nil +} + +func syncDaemonSet(ctx context.Context, client k8sclient.Client, scheme *runtime.Scheme, cr *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList, in *appsv1.DaemonSet) error { + logger := log.Log.WithName("syncDaemonSet") + logger.V(1).Info("Start to sync DaemonSet", "Namespace", in.Namespace, "Name", in.Name) + var err error + + if pl != nil { + if err = setDsNodeAffinity(pl, in); err != nil { + return err + } + } + if err = controllerutil.SetControllerReference(cr, in, scheme); err != nil { + return err + } + ds := &appsv1.DaemonSet{} + err = client.Get(ctx, types.NamespacedName{Namespace: in.Namespace, Name: in.Name}, ds) + if err != nil { + if errors.IsNotFound(err) { + logger.V(1).Info("Created DaemonSet", in.Namespace, in.Name) + err = client.Create(ctx, in) + if err != nil { + logger.Error(err, "Fail to create Daemonset", "Namespace", in.Namespace, "Name", in.Name) + return err + } + } else { + logger.Error(err, "Fail to get Daemonset", "Namespace", in.Namespace, "Name", in.Name) + return err + } + } else { + logger.V(1).Info("DaemonSet already exists, updating") + // DeepDerivative checks for changes only comparing non-zero fields in the source struct. + // This skips default values added by the api server. + // References in https://github.com/kubernetes-sigs/kubebuilder/issues/592#issuecomment-625738183 + if equality.Semantic.DeepDerivative(in.Spec, ds.Spec) { + // DeepDerivative has issue detecting nodeAffinity change + // https://bugzilla.redhat.com/show_bug.cgi?id=1914066 + // DeepDerivative doesn't detect changes in containers args section + // This code should be fixed both with NodeAffinity comparation + if equality.Semantic.DeepEqual(in.Spec.Template.Spec.Affinity.NodeAffinity, + ds.Spec.Template.Spec.Affinity.NodeAffinity) && + equality.Semantic.DeepEqual(in.Spec.Template.Spec.Containers[0].Args, + ds.Spec.Template.Spec.Containers[0].Args) { + logger.V(1).Info("Daemonset spec did not change, not updating") + return nil + } + } + err = client.Update(ctx, in) + if err != nil { + logger.Error(err, "Fail to update DaemonSet", "Namespace", in.Namespace, "Name", in.Name) + return err + } + } + return nil +} diff --git a/controllers/sriovibnetwork_controller.go b/controllers/sriovibnetwork_controller.go index 0ffe5fba0..65b66f167 100644 --- a/controllers/sriovibnetwork_controller.go +++ b/controllers/sriovibnetwork_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // SriovIBNetworkReconciler reconciles a SriovIBNetwork object @@ -58,7 +59,7 @@ type SriovIBNetworkReconciler struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile func (r *SriovIBNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // The SriovNetwork CR shall only be defined in operator namespace. - req.Namespace = namespace + req.Namespace = vars.Namespace reqLogger := log.FromContext(ctx).WithValues("sriovnetwork", req.NamespacedName) reqLogger.Info("Reconciling SriovIBNetwork") var err error diff --git a/controllers/sriovibnetwork_controller_test.go b/controllers/sriovibnetwork_controller_test.go index fa676c369..6ba0d2bd8 100644 --- a/controllers/sriovibnetwork_controller_test.go +++ b/controllers/sriovibnetwork_controller_test.go @@ -223,11 +223,18 @@ var _ = Describe("SriovIBNetwork Controller", func() { err = k8sClient.Get(ctx, types.NamespacedName{Name: cr.GetName(), Namespace: "ib-ns-xxx"}, netAttDef) Expect(err).To(HaveOccurred()) - err = k8sClient.Create(goctx.TODO(), &corev1.Namespace{ + // Create Namespace + nsObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "ib-ns-xxx"}, - }) + } + err = k8sClient.Create(goctx.TODO(), nsObj) Expect(err).NotTo(HaveOccurred()) + DeferCleanup(func() { + err = k8sClient.Delete(goctx.TODO(), nsObj) + Expect(err).NotTo(HaveOccurred()) + }) + // Check that net-attach-def has been created err = util.WaitForNamespacedObject(netAttDef, k8sClient, "ib-ns-xxx", cr.GetName(), util.RetryInterval, util.Timeout) Expect(err).NotTo(HaveOccurred()) diff --git a/controllers/sriovnetwork_controller.go b/controllers/sriovnetwork_controller.go index f89d524c1..7ef991e93 100644 --- a/controllers/sriovnetwork_controller.go +++ b/controllers/sriovnetwork_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // SriovNetworkReconciler reconciles a SriovNetwork object @@ -57,7 +58,7 @@ type SriovNetworkReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile func (r *SriovNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - req.Namespace = namespace + req.Namespace = vars.Namespace reqLogger := log.FromContext(ctx).WithValues("sriovnetwork", req.NamespacedName) reqLogger.Info("Reconciling SriovNetwork") diff --git a/controllers/sriovnetwork_controller_test.go b/controllers/sriovnetwork_controller_test.go index f4eb42020..9f2e5a6ae 100644 --- a/controllers/sriovnetwork_controller_test.go +++ b/controllers/sriovnetwork_controller_test.go @@ -260,11 +260,18 @@ var _ = Describe("SriovNetwork Controller", func() { err = k8sClient.Get(ctx, types.NamespacedName{Name: cr.GetName(), Namespace: "ns-xxx"}, netAttDef) Expect(err).To(HaveOccurred()) - err = k8sClient.Create(goctx.TODO(), &corev1.Namespace{ + // Create Namespace + nsObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "ns-xxx"}, - }) + } + err = k8sClient.Create(goctx.TODO(), nsObj) Expect(err).NotTo(HaveOccurred()) + DeferCleanup(func() { + err = k8sClient.Delete(goctx.TODO(), nsObj) + Expect(err).NotTo(HaveOccurred()) + }) + // Check that net-attach-def has been created err = util.WaitForNamespacedObject(netAttDef, k8sClient, "ns-xxx", cr.GetName(), util.RetryInterval, util.Timeout) Expect(err).NotTo(HaveOccurred()) diff --git a/controllers/sriovnetworknodepolicy_controller.go b/controllers/sriovnetworknodepolicy_controller.go index 3d1da743f..fa67a54aa 100644 --- a/controllers/sriovnetworknodepolicy_controller.go +++ b/controllers/sriovnetworknodepolicy_controller.go @@ -20,24 +20,19 @@ import ( "context" "encoding/json" "fmt" - "os" "sort" "strings" "time" - utils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" - errs "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - kscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -47,10 +42,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" + utils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + dptypes "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" ) @@ -86,11 +83,11 @@ func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ct reqLogger.Info("Reconciling") defaultPolicy := &sriovnetworkv1.SriovNetworkNodePolicy{} - err := r.Get(ctx, types.NamespacedName{Name: constants.DefaultPolicyName, Namespace: namespace}, defaultPolicy) + err := r.Get(ctx, types.NamespacedName{Name: constants.DefaultPolicyName, Namespace: vars.Namespace}, defaultPolicy) if err != nil { if errors.IsNotFound(err) { // Default policy object not found, create it. - defaultPolicy.SetNamespace(namespace) + defaultPolicy.SetNamespace(vars.Namespace) defaultPolicy.SetName(constants.DefaultPolicyName) defaultPolicy.Spec = sriovnetworkv1.SriovNetworkNodePolicySpec{ NumVfs: 0, @@ -99,7 +96,7 @@ func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ct } err = r.Create(ctx, defaultPolicy) if err != nil { - reqLogger.Error(err, "Failed to create default Policy", "Namespace", namespace, "Name", constants.DefaultPolicyName) + reqLogger.Error(err, "Failed to create default Policy", "Namespace", vars.Namespace, "Name", constants.DefaultPolicyName) return reconcile.Result{}, err } reqLogger.Info("Default policy created") @@ -129,7 +126,7 @@ func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ct "kubernetes.io/os": "linux", } defaultOpConf := &sriovnetworkv1.SriovOperatorConfig{} - if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: constants.DefaultConfigName}, defaultOpConf); err != nil { + if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.DefaultConfigName}, defaultOpConf); err != nil { return reconcile.Result{}, err } if len(defaultOpConf.Spec.ConfigDaemonNodeSelector) > 0 { @@ -154,7 +151,7 @@ func (r *SriovNetworkNodePolicyReconciler) Reconcile(ctx context.Context, req ct return reconcile.Result{}, err } // Render and sync Daemon objects - if err = r.syncPluginDaemonObjs(ctx, defaultOpConf, defaultPolicy, policyList); err != nil { + if err = syncPluginDaemonObjs(ctx, r.Client, r.Scheme, defaultPolicy, policyList); err != nil { return reconcile.Result{}, err } @@ -220,7 +217,7 @@ func (r *SriovNetworkNodePolicyReconciler) syncDevicePluginConfigMap(ctx context }, ObjectMeta: metav1.ObjectMeta{ Name: constants.ConfigMapName, - Namespace: namespace, + Namespace: vars.Namespace, }, Data: configData, } @@ -250,14 +247,14 @@ func (r *SriovNetworkNodePolicyReconciler) syncAllSriovNetworkNodeStates(ctx con logger := log.Log.WithName("syncAllSriovNetworkNodeStates") logger.V(1).Info("Start to sync all SriovNetworkNodeState custom resource") found := &corev1.ConfigMap{} - if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: constants.ConfigMapName}, found); err != nil { + if err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: constants.ConfigMapName}, found); err != nil { logger.V(1).Info("Fail to get", "ConfigMap", constants.ConfigMapName) } for _, node := range nl.Items { logger.V(1).Info("Sync SriovNetworkNodeState CR", "name", node.Name) ns := &sriovnetworkv1.SriovNetworkNodeState{} ns.Name = node.Name - ns.Namespace = namespace + ns.Namespace = vars.Namespace j, _ := json.Marshal(ns) logger.V(2).Info("SriovNetworkNodeState CR", "content", j) if err := r.syncSriovNetworkNodeState(ctx, np, npl, ns, &node, utils.HashConfigMap(found)); err != nil { @@ -362,203 +359,6 @@ func (r *SriovNetworkNodePolicyReconciler) syncSriovNetworkNodeState(ctx context return nil } -func (r *SriovNetworkNodePolicyReconciler) syncPluginDaemonObjs(ctx context.Context, operatorConfig *sriovnetworkv1.SriovOperatorConfig, dp *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList) error { - logger := log.Log.WithName("syncPluginDaemonObjs") - logger.V(1).Info("Start to sync sriov daemons objects") - - // render plugin manifests - data := render.MakeRenderData() - data.Data["Namespace"] = namespace - data.Data["SRIOVDevicePluginImage"] = os.Getenv("SRIOV_DEVICE_PLUGIN_IMAGE") - data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION") - data.Data["ResourcePrefix"] = os.Getenv("RESOURCE_PREFIX") - data.Data["ImagePullSecrets"] = GetImagePullSecrets() - data.Data["NodeSelectorField"] = GetDefaultNodeSelector() - data.Data["UseCDI"] = operatorConfig.Spec.UseCDI - - objs, err := renderDsForCR(constants.PluginPath, &data) - if err != nil { - logger.Error(err, "Fail to render SR-IoV manifests") - return err - } - - defaultConfig := &sriovnetworkv1.SriovOperatorConfig{} - err = r.Get(ctx, types.NamespacedName{ - Name: constants.DefaultConfigName, Namespace: namespace}, defaultConfig) - if err != nil { - return err - } - - if len(pl.Items) < 2 { - for _, obj := range objs { - err := r.deleteK8sResource(ctx, obj) - if err != nil { - return err - } - } - return nil - } - - // Sync DaemonSets - for _, obj := range objs { - if obj.GetKind() == constants.DaemonSet && len(defaultConfig.Spec.ConfigDaemonNodeSelector) > 0 { - scheme := kscheme.Scheme - ds := &appsv1.DaemonSet{} - err = scheme.Convert(obj, ds, nil) - if err != nil { - logger.Error(err, "Fail to convert to DaemonSet") - return err - } - ds.Spec.Template.Spec.NodeSelector = defaultConfig.Spec.ConfigDaemonNodeSelector - err = scheme.Convert(ds, obj, nil) - if err != nil { - logger.Error(err, "Fail to convert to Unstructured") - return err - } - } - err = r.syncDsObject(ctx, dp, pl, obj) - if err != nil { - logger.Error(err, "Couldn't sync SR-IoV daemons objects") - return err - } - } - - // Sriov-cni container has been moved to sriov-network-config-daemon DaemonSet. - // Delete stale sriov-cni manifests. Revert this change once sriov-cni daemonSet - // is deprecated. - err = r.deleteSriovCniManifests(ctx) - if err != nil { - return err - } - - return nil -} - -func (r *SriovNetworkNodePolicyReconciler) deleteSriovCniManifests(ctx context.Context) error { - ds := &appsv1.DaemonSet{} - err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: "sriov-cni"}, ds) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - } else { - err = r.Delete(ctx, ds) - if err != nil { - return err - } - } - - rb := &rbacv1.RoleBinding{} - err = r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: "sriov-cni"}, rb) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - } else { - err = r.Delete(ctx, rb) - if err != nil { - return err - } - } - - sa := &corev1.ServiceAccount{} - err = r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: "sriov-cni"}, sa) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - } else { - err = r.Delete(ctx, sa) - if err != nil { - return err - } - } - - return nil -} - -func (r *SriovNetworkNodePolicyReconciler) deleteK8sResource(ctx context.Context, in *uns.Unstructured) error { - if err := apply.DeleteObject(ctx, r.Client, in); err != nil { - return fmt.Errorf("failed to delete object %v with err: %v", in, err) - } - return nil -} - -func (r *SriovNetworkNodePolicyReconciler) syncDsObject(ctx context.Context, dp *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList, obj *uns.Unstructured) error { - logger := log.Log.WithName("syncDsObject") - kind := obj.GetKind() - logger.V(1).Info("Start to sync Objects", "Kind", kind) - switch kind { - case "ServiceAccount", "Role", "RoleBinding": - if err := controllerutil.SetControllerReference(dp, obj, r.Scheme); err != nil { - return err - } - if err := apply.ApplyObject(ctx, r.Client, obj); err != nil { - logger.Error(err, "Fail to sync", "Kind", kind) - return err - } - case constants.DaemonSet: - ds := &appsv1.DaemonSet{} - err := r.Scheme.Convert(obj, ds, nil) - r.syncDaemonSet(ctx, dp, pl, ds) - if err != nil { - logger.Error(err, "Fail to sync DaemonSet", "Namespace", ds.Namespace, "Name", ds.Name) - return err - } - } - return nil -} - -func (r *SriovNetworkNodePolicyReconciler) syncDaemonSet(ctx context.Context, cr *sriovnetworkv1.SriovNetworkNodePolicy, pl *sriovnetworkv1.SriovNetworkNodePolicyList, in *appsv1.DaemonSet) error { - logger := log.Log.WithName("syncDaemonSet") - logger.V(1).Info("Start to sync DaemonSet", "Namespace", in.Namespace, "Name", in.Name) - var err error - - if pl != nil { - if err = setDsNodeAffinity(pl, in); err != nil { - return err - } - } - if err = controllerutil.SetControllerReference(cr, in, r.Scheme); err != nil { - return err - } - ds := &appsv1.DaemonSet{} - err = r.Get(ctx, types.NamespacedName{Namespace: in.Namespace, Name: in.Name}, ds) - if err != nil { - if errors.IsNotFound(err) { - logger.Info("Created DaemonSet", in.Namespace, in.Name) - err = r.Create(ctx, in) - if err != nil { - logger.Error(err, "Fail to create Daemonset", "Namespace", in.Namespace, "Name", in.Name) - return err - } - } else { - logger.Error(err, "Fail to get Daemonset", "Namespace", in.Namespace, "Name", in.Name) - return err - } - } else { - logger.V(1).Info("DaemonSet already exists, updating") - // DeepDerivative checks for changes only comparing non zero fields in the source struct. - // This skips default values added by the api server. - // References in https://github.com/kubernetes-sigs/kubebuilder/issues/592#issuecomment-625738183 - if equality.Semantic.DeepDerivative(in.Spec, ds.Spec) { - // DeepDerivative has issue detecting nodeAffinity change - // https://bugzilla.redhat.com/show_bug.cgi?id=1914066 - if equality.Semantic.DeepEqual(in.Spec.Template.Spec.Affinity.NodeAffinity, - ds.Spec.Template.Spec.Affinity.NodeAffinity) { - logger.V(1).Info("Daemonset spec did not change, not updating") - return nil - } - } - err = r.Update(ctx, in) - if err != nil { - logger.Error(err, "Fail to update DaemonSet", "Namespace", in.Namespace, "Name", in.Name) - return err - } - } - return nil -} - func setDsNodeAffinity(pl *sriovnetworkv1.SriovNetworkNodePolicyList, ds *appsv1.DaemonSet) error { terms := nodeSelectorTermsForPolicyList(pl.Items) if len(terms) > 0 { @@ -609,7 +409,7 @@ func renderDsForCR(path string, data *render.RenderData) ([]*uns.Unstructured, e objs, err := render.RenderDir(path, data) if err != nil { - return nil, errs.Wrap(err, "failed to render OpenShiftSRIOV Network manifests") + return nil, errs.Wrap(err, "failed to render SR-IOV Network Operator manifests") } return objs, nil } @@ -629,7 +429,7 @@ func (r *SriovNetworkNodePolicyReconciler) renderDevicePluginConfigData(ctx cont } nodeState := &sriovnetworkv1.SriovNetworkNodeState{} - err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: node.Name}, nodeState) + err := r.Get(ctx, types.NamespacedName{Namespace: vars.Namespace, Name: node.Name}, nodeState) if err != nil { return rcl, err } diff --git a/controllers/sriovnetworknodepolicy_controller_test.go b/controllers/sriovnetworknodepolicy_controller_test.go index bd4579d78..3894f5a93 100644 --- a/controllers/sriovnetworknodepolicy_controller_test.go +++ b/controllers/sriovnetworknodepolicy_controller_test.go @@ -18,6 +18,7 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) func TestNodeSelectorMerge(t *testing.T) { @@ -213,7 +214,7 @@ func TestRenderDevicePluginConfigData(t *testing.T) { reconciler := SriovNetworkNodePolicyReconciler{} node := corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1"}} - nodeState := sriovnetworkv1.SriovNetworkNodeState{ObjectMeta: metav1.ObjectMeta{Name: node.Name, Namespace: namespace}} + nodeState := sriovnetworkv1.SriovNetworkNodeState{ObjectMeta: metav1.ObjectMeta{Name: node.Name, Namespace: vars.Namespace}} scheme := runtime.NewScheme() utilruntime.Must(sriovnetworkv1.AddToScheme(scheme)) diff --git a/controllers/sriovnetworkpoolconfig_controller.go b/controllers/sriovnetworkpoolconfig_controller.go index de8fb32b2..211fdd9cf 100644 --- a/controllers/sriovnetworkpoolconfig_controller.go +++ b/controllers/sriovnetworkpoolconfig_controller.go @@ -17,17 +17,22 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // SriovNetworkPoolConfigReconciler reconciles a SriovNetworkPoolConfig object type SriovNetworkPoolConfigReconciler struct { client.Client - Scheme *runtime.Scheme - OpenshiftContext *utils.OpenshiftContext + Scheme *runtime.Scheme + PlatformHelper platforms.Interface } +const ( + mcPriority string = "00-" +) + //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworkpoolconfigs,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworkpoolconfigs/status,verbs=get;update;patch //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworkpoolconfigs/finalizers,verbs=update @@ -44,8 +49,8 @@ type SriovNetworkPoolConfigReconciler struct { func (r *SriovNetworkPoolConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx).WithValues("sriovnetworkpoolconfig", req.NamespacedName) isHypershift := false - if r.OpenshiftContext.IsOpenshiftCluster() { - if r.OpenshiftContext.IsHypershift() { + if r.PlatformHelper.IsOpenshiftCluster() { + if r.PlatformHelper.IsHypershift() { isHypershift = true } logger = logger.WithValues("isHypershift", isHypershift) @@ -78,7 +83,7 @@ func (r *SriovNetworkPoolConfigReconciler) Reconcile(ctx context.Context, req ct return reconcile.Result{}, err } } - if utils.ClusterType == utils.ClusterTypeOpenshift { + if vars.ClusterType == constants.ClusterTypeOpenshift { if !isHypershift { if err = r.syncOvsHardwareOffloadMachineConfigs(ctx, instance, false); err != nil { return reconcile.Result{}, err @@ -92,7 +97,7 @@ func (r *SriovNetworkPoolConfigReconciler) Reconcile(ctx context.Context, req ct if sriovnetworkv1.StringInArray(sriovnetworkv1.POOLCONFIGFINALIZERNAME, instance.ObjectMeta.Finalizers) { // our finalizer is present, so lets handle any external dependency logger.Info("delete SriovNetworkPoolConfig CR", "Namespace", instance.Namespace, "Name", instance.Name) - if utils.ClusterType == utils.ClusterTypeOpenshift && !isHypershift { + if vars.ClusterType == constants.ClusterTypeOpenshift && !isHypershift { if err = r.syncOvsHardwareOffloadMachineConfigs(ctx, instance, true); err != nil { // if fail to delete the external dependency here, return with error // so that it can be retried @@ -125,7 +130,7 @@ func (r *SriovNetworkPoolConfigReconciler) syncOvsHardwareOffloadMachineConfigs( logger := log.Log.WithName("syncOvsHardwareOffloadMachineConfigs") mcpName := nc.Spec.OvsHardwareOffloadConfig.Name - mcName := "00-" + mcpName + "-" + constants.OVSHWOLMachineConfigNameSuffix + mcName := mcPriority + mcpName + "-" + constants.OVSHWOLMachineConfigNameSuffix foundMC := &mcfgv1.MachineConfig{} mcp := &mcfgv1.MachineConfigPool{} diff --git a/controllers/sriovnetworkpoolconfig_controller_test.go b/controllers/sriovnetworkpoolconfig_controller_test.go index 5b76d1d19..d0a7090c5 100644 --- a/controllers/sriovnetworkpoolconfig_controller_test.go +++ b/controllers/sriovnetworkpoolconfig_controller_test.go @@ -13,12 +13,19 @@ import ( mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util" ) var _ = Describe("Operator", func() { Context("When is up", func() { It("should be able to create machine config for MachineConfigPool specified in sriov pool config", func() { + if vars.ClusterType != consts.ClusterTypeOpenshift { + Skip("test should only be executed with openshift cluster type") + } + config := &sriovnetworkv1.SriovNetworkPoolConfig{} config.SetNamespace(testNamespace) config.SetName("ovs-hw-offload-config") @@ -46,12 +53,21 @@ var _ = Describe("Operator", func() { } err = k8sClient.Create(goctx.TODO(), mcp) Expect(err).NotTo(HaveOccurred()) + DeferCleanup(func() { + err = k8sClient.Delete(goctx.TODO(), mcp) + Expect(err).ToNot(HaveOccurred()) + }) config.Spec.OvsHardwareOffloadConfig = sriovnetworkv1.OvsHardwareOffloadConfig{ Name: mcpName, } err = k8sClient.Create(goctx.TODO(), config) Expect(err).NotTo(HaveOccurred()) + DeferCleanup(func() { + err = k8sClient.Delete(goctx.TODO(), config) + Expect(err).ToNot(HaveOccurred()) + }) + Eventually(func() error { mc := &mcfgv1.MachineConfig{} err := k8sClient.Get(goctx.TODO(), types.NamespacedName{Name: mcName, Namespace: testNamespace}, mc) @@ -59,7 +75,7 @@ var _ = Describe("Operator", func() { return err } return nil - }, timeout*3, interval).Should(Succeed()) + }, util.APITimeout*3, util.RetryInterval).Should(Succeed()) }) }) }) diff --git a/controllers/sriovoperatorconfig_controller.go b/controllers/sriovoperatorconfig_controller.go index 4c453b6de..d75371581 100644 --- a/controllers/sriovoperatorconfig_controller.go +++ b/controllers/sriovoperatorconfig_controller.go @@ -39,17 +39,19 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" apply "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + consts "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" render "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render" utils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // SriovOperatorConfigReconciler reconciles a SriovOperatorConfig object type SriovOperatorConfigReconciler struct { client.Client - Scheme *runtime.Scheme - OpenshiftContext *utils.OpenshiftContext + Scheme *runtime.Scheme + PlatformHelper platforms.Interface } //+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovoperatorconfigs,verbs=get;list;watch;create;update;patch;delete @@ -70,13 +72,12 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. logger.Info("Reconciling SriovOperatorConfig") - enableAdmissionController := os.Getenv("ADMISSION_CONTROLLERS_ENABLED") == trueString - if !enableAdmissionController { + if !vars.EnableAdmissionController { logger.Info("SR-IOV Network Resource Injector and Operator Webhook are disabled.") } defaultConfig := &sriovnetworkv1.SriovOperatorConfig{} err := r.Get(ctx, types.NamespacedName{ - Name: constants.DefaultConfigName, Namespace: namespace}, defaultConfig) + Name: consts.DefaultConfigName, Namespace: vars.Namespace}, defaultConfig) if err != nil { if apierrors.IsNotFound(err) { singleNode, err := utils.IsSingleNodeCluster(r.Client) @@ -85,11 +86,11 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. } // Default Config object not found, create it. - defaultConfig.SetNamespace(namespace) - defaultConfig.SetName(constants.DefaultConfigName) + defaultConfig.SetNamespace(vars.Namespace) + defaultConfig.SetName(consts.DefaultConfigName) defaultConfig.Spec = sriovnetworkv1.SriovOperatorConfigSpec{ - EnableInjector: func() *bool { b := enableAdmissionController; return &b }(), - EnableOperatorWebhook: func() *bool { b := enableAdmissionController; return &b }(), + EnableInjector: func() *bool { b := vars.EnableAdmissionController; return &b }(), + EnableOperatorWebhook: func() *bool { b := vars.EnableAdmissionController; return &b }(), ConfigDaemonNodeSelector: map[string]string{}, LogLevel: 2, DisableDrain: singleNode, @@ -99,7 +100,7 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. err = r.Create(ctx, defaultConfig) if err != nil { logger.Error(err, "Failed to create default Operator Config", "Namespace", - namespace, "Name", constants.DefaultConfigName) + vars.Namespace, "Name", consts.DefaultConfigName) return reconcile.Result{}, err } return reconcile.Result{}, nil @@ -108,7 +109,22 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. return reconcile.Result{}, err } - if req.Namespace != namespace { + // Fetch the SriovNetworkNodePolicyList + policyList := &sriovnetworkv1.SriovNetworkNodePolicyList{} + err = r.List(ctx, policyList, &client.ListOptions{}) + if err != nil { + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + defaultPolicy := &sriovnetworkv1.SriovNetworkNodePolicy{} + err = r.Get(ctx, types.NamespacedName{Name: consts.DefaultPolicyName, Namespace: vars.Namespace}, defaultPolicy) + if err != nil { + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + if req.Namespace != vars.Namespace { return reconcile.Result{}, nil } @@ -122,16 +138,16 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. return reconcile.Result{}, err } - if err = r.syncPluginDaemonSet(ctx, defaultConfig); err != nil { + if err = syncPluginDaemonObjs(ctx, r.Client, r.Scheme, defaultPolicy, policyList); err != nil { return reconcile.Result{}, err } snolog.SetLogLevel(defaultConfig.Spec.LogLevel) // For Openshift we need to create the systemd files using a machine config - if utils.ClusterType == utils.ClusterTypeOpenshift { + if vars.ClusterType == consts.ClusterTypeOpenshift { // TODO: add support for hypershift as today there is no MCO on hypershift clusters - if r.OpenshiftContext.IsHypershift() { + if r.PlatformHelper.IsHypershift() { return ctrl.Result{}, fmt.Errorf("systemd mode is not supported on hypershift") } @@ -139,7 +155,7 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl. return reconcile.Result{}, err } } - return reconcile.Result{RequeueAfter: constants.ResyncPeriod}, nil + return reconcile.Result{RequeueAfter: consts.ResyncPeriod}, nil } // SetupWithManager sets up the controller with the Manager. @@ -151,48 +167,17 @@ func (r *SriovOperatorConfigReconciler) SetupWithManager(mgr ctrl.Manager) error Complete(r) } -func (r *SriovOperatorConfigReconciler) syncPluginDaemonSet(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig) error { - logger := log.Log.WithName("syncConfigDaemonset") - logger.V(1).Info("Start to sync SRIOV plugin daemonsets nodeSelector") - ds := &appsv1.DaemonSet{} - - names := []string{"sriov-cni", "sriov-device-plugin"} - - for _, name := range names { - err := r.Client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, ds) - if err != nil { - if apierrors.IsNotFound(err) { - continue - } - logger.Error(err, "Couldn't get daemonset", "name", name) - return err - } - if len(dc.Spec.ConfigDaemonNodeSelector) == 0 { - ds.Spec.Template.Spec.NodeSelector = GetDefaultNodeSelector() - } else { - ds.Spec.Template.Spec.NodeSelector = dc.Spec.ConfigDaemonNodeSelector - } - err = r.Client.Update(ctx, ds) - if err != nil { - logger.Error(err, "Couldn't update daemonset", "name", name) - return err - } - } - - return nil -} - func (r *SriovOperatorConfigReconciler) syncConfigDaemonSet(ctx context.Context, dc *sriovnetworkv1.SriovOperatorConfig) error { logger := log.Log.WithName("syncConfigDaemonset") logger.V(1).Info("Start to sync config daemonset") data := render.MakeRenderData() data.Data["Image"] = os.Getenv("SRIOV_NETWORK_CONFIG_DAEMON_IMAGE") - data.Data["Namespace"] = namespace + data.Data["Namespace"] = vars.Namespace data.Data["SRIOVCNIImage"] = os.Getenv("SRIOV_CNI_IMAGE") data.Data["SRIOVInfiniBandCNIImage"] = os.Getenv("SRIOV_INFINIBAND_CNI_IMAGE") data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION") - data.Data["ClusterType"] = utils.ClusterType + data.Data["ClusterType"] = vars.ClusterType data.Data["DevMode"] = os.Getenv("DEV_MODE") data.Data["ImagePullSecrets"] = GetImagePullSecrets() if dc.Spec.ConfigurationMode == sriovnetworkv1.SystemdConfigurationMode { @@ -208,7 +193,7 @@ func (r *SriovOperatorConfigReconciler) syncConfigDaemonSet(ctx context.Context, logger.V(1).Info("New cni bin found", "CNIBinPath", envCniBinPath) data.Data["CNIBinPath"] = envCniBinPath } - objs, err := render.RenderDir(constants.ConfigDaemonPath, &data) + objs, err := render.RenderDir(consts.ConfigDaemonPath, &data) if err != nil { logger.Error(err, "Fail to render config daemon manifests") return err @@ -246,12 +231,12 @@ func (r *SriovOperatorConfigReconciler) syncWebhookObjs(ctx context.Context, dc for name, path := range webhooks { // Render Webhook manifests data := render.MakeRenderData() - data.Data["Namespace"] = namespace + data.Data["Namespace"] = vars.Namespace data.Data["SRIOVMutatingWebhookName"] = name data.Data["NetworkResourcesInjectorImage"] = os.Getenv("NETWORK_RESOURCES_INJECTOR_IMAGE") data.Data["SriovNetworkWebhookImage"] = os.Getenv("SRIOV_NETWORK_WEBHOOK_IMAGE") data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION") - data.Data["ClusterType"] = utils.ClusterType + data.Data["ClusterType"] = vars.ClusterType data.Data["DevMode"] = os.Getenv("DEV_MODE") data.Data["ImagePullSecrets"] = GetImagePullSecrets() data.Data["CertManagerEnabled"] = strings.ToLower(os.Getenv("ADMISSION_CONTROLLERS_CERTIFICATES_CERT_MANAGER_ENABLED")) == trueString @@ -261,8 +246,8 @@ func (r *SriovOperatorConfigReconciler) syncWebhookObjs(ctx context.Context, dc data.Data["InjectorWebhookCA"] = os.Getenv("ADMISSION_CONTROLLERS_CERTIFICATES_INJECTOR_CA_CRT") data.Data["ExternalControlPlane"] = false - if r.OpenshiftContext.IsOpenshiftCluster() { - external := r.OpenshiftContext.IsHypershift() + if r.PlatformHelper.IsOpenshiftCluster() { + external := r.PlatformHelper.IsHypershift() data.Data["ExternalControlPlane"] = external } @@ -273,7 +258,7 @@ func (r *SriovOperatorConfigReconciler) syncWebhookObjs(ctx context.Context, dc } // Delete injector webhook - if !*dc.Spec.EnableInjector && path == constants.InjectorWebHookPath { + if !*dc.Spec.EnableInjector && path == consts.InjectorWebHookPath { for _, obj := range objs { err = r.deleteWebhookObject(ctx, obj) if err != nil { @@ -286,7 +271,7 @@ func (r *SriovOperatorConfigReconciler) syncWebhookObjs(ctx context.Context, dc continue } // Delete operator webhook - if !*dc.Spec.EnableOperatorWebhook && path == constants.OperatorWebHookPath { + if !*dc.Spec.EnableOperatorWebhook && path == consts.OperatorWebHookPath { for _, obj := range objs { err = r.deleteWebhookObject(ctx, obj) if err != nil { @@ -347,7 +332,7 @@ func (r *SriovOperatorConfigReconciler) syncOpenShiftSystemdService(ctx context. if cr.Spec.ConfigurationMode != sriovnetworkv1.SystemdConfigurationMode { obj := &machinev1.MachineConfig{} - err := r.Get(context.TODO(), types.NamespacedName{Name: constants.SystemdServiceOcpMachineConfigName}, obj) + err := r.Get(context.TODO(), types.NamespacedName{Name: consts.SystemdServiceOcpMachineConfigName}, obj) if err != nil { if apierrors.IsNotFound(err) { return nil @@ -370,7 +355,7 @@ func (r *SriovOperatorConfigReconciler) syncOpenShiftSystemdService(ctx context. logger.Info("Start to sync config systemd machine config for openshift") data := render.MakeRenderData() data.Data["LogLevel"] = cr.Spec.LogLevel - objs, err := render.RenderDir(constants.SystemdServiceOcpPath, &data) + objs, err := render.RenderDir(consts.SystemdServiceOcpPath, &data) if err != nil { logger.Error(err, "Fail to render config daemon manifests") return err diff --git a/controllers/sriovoperatorconfig_controller_test.go b/controllers/sriovoperatorconfig_controller_test.go index c925324b9..c2e779ee1 100644 --- a/controllers/sriovoperatorconfig_controller_test.go +++ b/controllers/sriovoperatorconfig_controller_test.go @@ -15,30 +15,10 @@ import ( ) var _ = Describe("Operator", func() { - var config *sriovnetworkv1.SriovOperatorConfig - // BeforeEach(func() { - // config = &sriovnetworkv1.SriovOperatorConfig{} - // config.SetNamespace(testNamespace) - // config.SetName(DEFAULT_CONFIG_NAME) - // config.Spec = sriovnetworkv1.SriovOperatorConfigSpec{ - // EnableInjector: func() *bool { b := true; return &b }(), - // EnableOperatorWebhook: func() *bool { b := true; return &b }(), - // ConfigDaemonNodeSelector: map[string]string{}, - // LogLevel: 2, - // } - // Expect(k8sClient.Create(goctx.TODO(), config)).Should(Succeed()) - // }) - // AfterEach(func() { - // config := &sriovnetworkv1.SriovOperatorConfig{} - // config.SetNamespace(testNamespace) - // config.SetName(DEFAULT_CONFIG_NAME) - // Expect(k8sClient.Delete(goctx.TODO(), config)).Should(Succeed()) - // }) - Context("When is up", func() { JustBeforeEach(func() { - config = &sriovnetworkv1.SriovOperatorConfig{} - err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + config := &sriovnetworkv1.SriovOperatorConfig{} + err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) config.Spec = sriovnetworkv1.SriovOperatorConfigSpec{ EnableInjector: func() *bool { b := true; return &b }(), @@ -52,11 +32,11 @@ var _ = Describe("Operator", func() { It("should have webhook enable", func() { mutateCfg := &admv1.MutatingWebhookConfiguration{} - err := util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err := util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) validateCfg := &admv1.ValidatingWebhookConfiguration{} - err = util.WaitForNamespacedObject(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err = util.WaitForNamespacedObject(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) }) @@ -64,7 +44,7 @@ var _ = Describe("Operator", func() { func(dsName string) { // wait for sriov-network-operator to be ready daemonSet := &appsv1.DaemonSet{} - err := util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, dsName, interval, timeout) + err := util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, dsName, util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) }, Entry("operator-webhook", "operator-webhook"), @@ -75,7 +55,7 @@ var _ = Describe("Operator", func() { It("should be able to turn network-resources-injector on/off", func() { By("set disable to enableInjector") config := &sriovnetworkv1.SriovOperatorConfig{} - err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) *config.Spec.EnableInjector = false @@ -83,15 +63,15 @@ var _ = Describe("Operator", func() { Expect(err).NotTo(HaveOccurred()) daemonSet := &appsv1.DaemonSet{} - err = util.WaitForNamespacedObjectDeleted(daemonSet, k8sClient, testNamespace, "network-resources-injector", interval, timeout) + err = util.WaitForNamespacedObjectDeleted(daemonSet, k8sClient, testNamespace, "network-resources-injector", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) mutateCfg := &admv1.MutatingWebhookConfiguration{} - err = util.WaitForNamespacedObjectDeleted(mutateCfg, k8sClient, testNamespace, "network-resources-injector-config", interval, timeout) + err = util.WaitForNamespacedObjectDeleted(mutateCfg, k8sClient, testNamespace, "network-resources-injector-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) By("set enable to enableInjector") - err = util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err = util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) *config.Spec.EnableInjector = true @@ -99,11 +79,11 @@ var _ = Describe("Operator", func() { Expect(err).NotTo(HaveOccurred()) daemonSet = &appsv1.DaemonSet{} - err = util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, "network-resources-injector", interval, timeout) + err = util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, "network-resources-injector", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) mutateCfg = &admv1.MutatingWebhookConfiguration{} - err = util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "network-resources-injector-config", interval, timeout) + err = util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "network-resources-injector-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) }) @@ -111,7 +91,7 @@ var _ = Describe("Operator", func() { By("set disable to enableOperatorWebhook") config := &sriovnetworkv1.SriovOperatorConfig{} - err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) *config.Spec.EnableOperatorWebhook = false @@ -119,19 +99,19 @@ var _ = Describe("Operator", func() { Expect(err).NotTo(HaveOccurred()) daemonSet := &appsv1.DaemonSet{} - err = util.WaitForNamespacedObjectDeleted(daemonSet, k8sClient, testNamespace, "operator-webhook", interval, timeout) + err = util.WaitForNamespacedObjectDeleted(daemonSet, k8sClient, testNamespace, "operator-webhook", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) mutateCfg := &admv1.MutatingWebhookConfiguration{} - err = util.WaitForNamespacedObjectDeleted(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err = util.WaitForNamespacedObjectDeleted(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) validateCfg := &admv1.ValidatingWebhookConfiguration{} - err = util.WaitForNamespacedObjectDeleted(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err = util.WaitForNamespacedObjectDeleted(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) By("set disable to enableOperatorWebhook") - err = util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err = util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) *config.Spec.EnableOperatorWebhook = true @@ -139,22 +119,22 @@ var _ = Describe("Operator", func() { Expect(err).NotTo(HaveOccurred()) daemonSet = &appsv1.DaemonSet{} - err = util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, "operator-webhook", interval, timeout) + err = util.WaitForNamespacedObject(daemonSet, k8sClient, testNamespace, "operator-webhook", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) mutateCfg = &admv1.MutatingWebhookConfiguration{} - err = util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err = util.WaitForNamespacedObject(mutateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) validateCfg = &admv1.ValidatingWebhookConfiguration{} - err = util.WaitForNamespacedObject(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", interval, timeout) + err = util.WaitForNamespacedObject(validateCfg, k8sClient, testNamespace, "sriov-operator-webhook-config", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) }) It("should be able to update the node selector of sriov-network-config-daemon", func() { By("specify the configDaemonNodeSelector") config := &sriovnetworkv1.SriovOperatorConfig{} - err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) config.Spec.ConfigDaemonNodeSelector = map[string]string{"node-role.kubernetes.io/worker": ""} err = k8sClient.Update(goctx.TODO(), config) @@ -168,13 +148,13 @@ var _ = Describe("Operator", func() { return nil } return daemonSet.Spec.Template.Spec.NodeSelector - }, timeout*10, interval).Should(Equal(config.Spec.ConfigDaemonNodeSelector)) + }, util.APITimeout*10, util.RetryInterval).Should(Equal(config.Spec.ConfigDaemonNodeSelector)) }) It("should be able to do multiple updates to the node selector of sriov-network-config-daemon", func() { By("changing the configDaemonNodeSelector") config := &sriovnetworkv1.SriovOperatorConfig{} - err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", interval, timeout) + err := util.WaitForNamespacedObject(config, k8sClient, testNamespace, "default", util.RetryInterval, util.APITimeout) Expect(err).NotTo(HaveOccurred()) config.Spec.ConfigDaemonNodeSelector = map[string]string{"labelA": "", "labelB": "", "labelC": ""} err = k8sClient.Update(goctx.TODO(), config) @@ -190,7 +170,7 @@ var _ = Describe("Operator", func() { return nil } return daemonSet.Spec.Template.Spec.NodeSelector - }, timeout*10, interval).Should(Equal(config.Spec.ConfigDaemonNodeSelector)) + }, util.APITimeout*10, util.RetryInterval).Should(Equal(config.Spec.ConfigDaemonNodeSelector)) }) }) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 4fe8a3eb8..13505c1a2 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -23,13 +23,11 @@ import ( "testing" "time" - netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - openshiftconfigv1 "github.com/openshift/api/config/v1" - mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" - "go.uber.org/zap/zapcore" + "go.uber.org/zap/zapcore" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" @@ -39,10 +37,16 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + openshiftconfigv1 "github.com/openshift/api/config/v1" + mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + + //+kubebuilder:scaffold:imports sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" - //+kubebuilder:scaffold:imports + mock_platforms "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/mock" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util" ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -57,14 +61,9 @@ var ( ) // Define utility constants for object names and testing timeouts/durations and intervals. -const ( - testNamespace = "openshift-sriov-network-operator" +const testNamespace = "openshift-sriov-network-operator" - timeout = time.Second * 10 - interval = time.Millisecond * 250 -) - -var _ = BeforeSuite(func(done Done) { +var _ = BeforeSuite(func() { logf.SetLogger(zap.New( zap.WriteTo(GinkgoWriter), zap.UseDevMode(true), @@ -129,17 +128,24 @@ var _ = BeforeSuite(func(done Done) { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + t := GinkgoT() + mockCtrl := gomock.NewController(t) + platformHelper := mock_platforms.NewMockInterface(mockCtrl) + platformHelper.EXPECT().GetFlavor().Return(openshift.OpenshiftFlavorDefault).AnyTimes() + platformHelper.EXPECT().IsOpenshiftCluster().Return(false).AnyTimes() + platformHelper.EXPECT().IsHypershift().Return(false).AnyTimes() + err = (&SriovOperatorConfigReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - OpenshiftContext: &utils.OpenshiftContext{OpenshiftFlavor: utils.OpenshiftFlavorDefault}, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + PlatformHelper: platformHelper, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) err = (&SriovNetworkPoolConfigReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - OpenshiftContext: &utils.OpenshiftContext{OpenshiftFlavor: utils.OpenshiftFlavorDefault}, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + PlatformHelper: platformHelper, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) @@ -159,12 +165,6 @@ var _ = BeforeSuite(func(done Done) { ctx, cancel = context.WithCancel(ctrl.SetupSignalHandler()) - go func() { - defer GinkgoRecover() - err = k8sManager.Start(ctx) - Expect(err).ToNot(HaveOccurred()) - }() - // Create test namespace ns := &corev1.Namespace{ TypeMeta: metav1.TypeMeta{}, @@ -176,6 +176,7 @@ var _ = BeforeSuite(func(done Done) { } Expect(k8sClient.Create(context.TODO(), ns)).Should(Succeed()) + // Create default SriovOperatorConfig config := &sriovnetworkv1.SriovOperatorConfig{} config.SetNamespace(testNamespace) config.SetName(constants.DefaultConfigName) @@ -187,6 +188,18 @@ var _ = BeforeSuite(func(done Done) { } Expect(k8sClient.Create(context.TODO(), config)).Should(Succeed()) + // Create default SriovNetworkNodePolicy + defaultPolicy := &sriovnetworkv1.SriovNetworkNodePolicy{} + defaultPolicy.SetNamespace(testNamespace) + defaultPolicy.SetName(constants.DefaultPolicyName) + defaultPolicy.Spec = sriovnetworkv1.SriovNetworkNodePolicySpec{ + NumVfs: 0, + NodeSelector: make(map[string]string), + NicSelector: sriovnetworkv1.SriovNetworkNicSelector{}, + } + Expect(k8sClient.Create(context.TODO(), defaultPolicy)).Should(Succeed()) + + // Create openshift Infrastructure infra := &openshiftconfigv1.Infrastructure{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -198,20 +211,28 @@ var _ = BeforeSuite(func(done Done) { } Expect(k8sClient.Create(context.TODO(), infra)).Should(Succeed()) + // Create default SriovNetworkPoolConfig poolConfig := &sriovnetworkv1.SriovNetworkPoolConfig{} poolConfig.SetNamespace(testNamespace) poolConfig.SetName(constants.DefaultConfigName) poolConfig.Spec = sriovnetworkv1.SriovNetworkPoolConfigSpec{} Expect(k8sClient.Create(context.TODO(), poolConfig)).Should(Succeed()) - close(done) + + go func() { + defer GinkgoRecover() + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred()) + }() }) var _ = AfterSuite(func() { By("tearing down the test environment") cancel() - Eventually(func() error { - return testEnv.Stop() - }, timeout, time.Second).ShouldNot(HaveOccurred()) + if testEnv != nil { + Eventually(func() error { + return testEnv.Stop() + }, util.APITimeout, time.Second).ShouldNot(HaveOccurred()) + } }) func TestAPIs(t *testing.T) { diff --git a/deployment/sriov-network-operator/Chart.yaml b/deployment/sriov-network-operator/Chart.yaml index 16ca4b1e0..8c7a62e95 100644 --- a/deployment/sriov-network-operator/Chart.yaml +++ b/deployment/sriov-network-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: sriov-network-operator version: 0.1.0 -kubeVersion: '>= 1.16.0' +kubeVersion: '>= 1.16.0-0' appVersion: 1.2.0 description: SR-IOV network operator configures and manages SR-IOV networks in the kubernetes cluster type: application diff --git a/deployment/sriov-network-operator/README.md b/deployment/sriov-network-operator/README.md index f0cdeed30..c0fba2f69 100644 --- a/deployment/sriov-network-operator/README.md +++ b/deployment/sriov-network-operator/README.md @@ -64,10 +64,45 @@ We have introduced the following Chart parameters. | Name | Type | Default | description | | ---- | ---- | ------- | ----------- | +| `operator.tolerations` | list | `[{"key":"node-role.kubernetes.io/master","operator":"Exists","effect":"NoSchedule"},{"key":"node-role.kubernetes.io/control-plane","operator":"Exists","effect":"NoSchedule"}]` | Operator's tolerations | +| `operator.nodeSelector` | object | {} | Operator's node selector | +| `operator.affinity` | object | `{"nodeAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":1,"preference":{"matchExpressions":[{"key":"node-role.kubernetes.io/master","operator":"In","values":[""]}]}},{"weight":1,"preference":{"matchExpressions":[{"key":"node-role.kubernetes.io/control-plane","operator":"In","values":[""]}]}}]}}` | Operator's afffinity configuration | +| `operator.nameOverride` | string | `` | Operator's resource name override | +| `operator.fullnameOverride` | string | `` | Operator's resource full name override | | `operator.resourcePrefix` | string | `openshift.io` | Device plugin resource prefix | -| `operator.enableAdmissionController` | bool | `false` | Enable SR-IOV network resource injector and operator webhook | | `operator.cniBinPath` | string | `/opt/cni/bin` | Path for CNI binary | -| `operator.clusterType` | string | `kubernetes` | Cluster environment type | +| `operator.clustertype` | string | `kubernetes` | Cluster environment type | + +#### Admission Controllers parameters + +The admission controllers can be enabled by switching on a single parameter `operator.admissionControllers.enabled`. By +default, the user needs to pre-create Kubernetes Secrets that match the names provided in +`operator.admissionControllers.certificates.secretNames`. The secrets should have 3 fields populated with the relevant +content: +* `ca.crt` (value needs to be base64 encoded twice) +* `tls.crt` +* `tls.key` + +Aside from the aforementioned mode, the chart supports 3 more modes for certificate consumption by the admission +controllers, which can be found in the table below. In a nutshell, the modes that are supported are: +* Consume pre-created Certificates managed by cert-manager +* Generate self signed Certificates managed by cert-manager +* Specify the content of the certificates as Helm values + +| Name | Type | Default | description | +| ---- | ---- | ------- | ----------- | +| `operator.admissionControllers.enabled` | bool | false | Flag that switches on the admission controllers | +| `operator.admissionControllers.certificates.secretNames.operator` | string | `operator-webhook-cert` | Secret that stores the certificate for the Operator's admission controller | +| `operator.admissionControllers.certificates.secretNames.injector` | string | `network-resources-injector-cert` | Secret that stores the certificate for the Network Resources Injector's admission controller | +| `operator.admissionControllers.certificates.certManager.enabled` | bool | false | Flag that switches on consumption of certificates managed by cert-manager | +| `operator.admissionControllers.certificates.certManager.generateSelfSigned` | bool | false | Flag that switches on generation of self signed certificates managed by cert-manager. The secrets in which the certificates are stored will have the names provided in `operator.admissionControllers.certificates.secretNames` | +| `operator.admissionControllers.certificates.custom.enabled` | bool | false | Flag that switches on consumption of user provided certificates that are part of `operator.admissionControllers.certificates.custom.operator` and `operator.admissionControllers.certificates.custom.injector` objects | +| `operator.admissionControllers.certificates.custom.operator.caCrt` | string | `` | The CA certificate to be used by the Operator's admission controller | +| `operator.admissionControllers.certificates.custom.operator.tlsCrt` | string | `` | The public part of the certificate to be used by the Operator's admission controller | +| `operator.admissionControllers.certificates.custom.operator.tlsKey` | string | `` | The private part of the certificate to be used by the Operator's admission controller | +| `operator.admissionControllers.certificates.custom.injector.caCrt` | string | `` | The CA certificate to be used by the Network Resources Injector's admission controller | +| `operator.admissionControllers.certificates.custom.injector.tlsCrt` | string | `` | The public part of the certificate to be used by the Network Resources Injector's admission controller | +| `operator.admissionControllers.certificates.custom.injector.tlsKey` | string | `` | The private part of the certificate to be used by the Network Resources Injector's admission controller | ### Images parameters diff --git a/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodestates.yaml b/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodestates.yaml index 8e756f681..e5674e3df 100644 --- a/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodestates.yaml +++ b/deployment/sriov-network-operator/crds/sriovnetwork.openshift.io_sriovnetworknodestates.yaml @@ -112,6 +112,8 @@ spec: type: string pciAddress: type: string + vdpaType: + type: string vendor: type: string vfID: diff --git a/doc/design/switchdev-refactoring.md b/doc/design/switchdev-refactoring.md new file mode 100644 index 000000000..ec0c0e93b --- /dev/null +++ b/doc/design/switchdev-refactoring.md @@ -0,0 +1,206 @@ +--- +title: switchdev mode refactoring +authors: + - ykulazhenkov +reviewers: +creation-date: 14-12-2023 +last-updated: 14-12-2023 +--- + +# switchdev and systemd modes refactoring + +## Summary + +We need to refactor the implementation used for NICs in switchdev mode and align its behavior with the systemd +mode of the operator. The refactoring is required to simplify the development of the new switchdev-related +features for the sriov-network-operator. + +## Motivation + +Currently the **sriov-network-operator** supports two configuration modes: +* `daemon` +* `systemd` + +The configuration mode can be changed by setting the `configurationMode` field in the `SriovOperatorConfig` CR. + +_**Note**: This setting is global and applies to all sriov-network-operator-daemons in the cluster._ + +In the `daemon` mode, which historically is the first implemented mode, +the operator will setup NICs with _**SRIOV legacy**_ configuration directly in the **sriov-network-operator-daemon** +component by executing all enabled plugins. + +When the operator is in `systemd` mode, the **sriov-network-operator-daemon** component will execute most plugins +in the same way as in the `daemon` mode but will skip the call of the *generic* or *virtual* +plugin(when running in a virtualized environment) and instead will render config for the systemd +service that starts on the next OS boot and calls one of these plugins. +Then, the result of the service execution is handled by the **sriov-network-operator-daemon**. + +The `systemd` mode was implemented to support scenarios when, after the host reboot, +we need VFs to be configured before Kubernetes (kubelet) and Pods with workloads are started. + +To setup NICs with _**switchdev**_ configuration, the operator uses a different flow that ignores +the `configurationMode` setting. Two systemd services(not the same as used for systemd mode) are created on the node. +The first service is executed before the NetworkManager starts, and the second one after. +Both services run bash scripts. The script from the first service is responsible for VFs creation and for +switching a NIC to the switchdev eSwitch mode. The script from the second service binds VFs to the required drivers. + +If a NIC has _**switchdev**_ configuration, then `configurationMode` of the operator does not affect it. + +```mermaid +--- +title: Current logic +--- +flowchart TD; + operatorMode["SriovOperatorConfig.configurationMode"]; + nicMode["NIC's eSwitch mode"]; + inDaemon["apply configuration(legacy) in + sriov-network-operator-daemon"] + bashSystemd["create two switchdev services(bash scripts) + to handle configuration(switchdev) on boot"]; + systemdSystemd["create single systemd service(go code) + to handle configuration(legacy) on boot"] + + nicMode-- switchdev --> bashSystemd; + nicMode-- legacy --> operatorMode; + operatorMode-- daemon --> inDaemon; + operatorMode-- systemd --> systemdSystemd; + +``` + +#### Problems of the current implementation + +* it is confusing that `configurationMode` does not affect devices with switchdev configuration. +* system services for switchdev configuration are shell scripts completely independent from the +main code base and it is hard to extend them with new functionality. +* for switchdev NICs, VF configuration flow (bash-based) has some limitations compared to legacy VF configuration. +* it is impossible to apply switchdev configuration for the NIC without reboot. + +### Use Cases + +* As a developer I don't want to maintain the code with similar logic in two places: +switchdev bash scrips and **sriov-network-operator-daemon** code. +* As a developer and a user I want to have only one set of systemd services that handle +both _**switchdev**_ configurations and `systemd` mode. +* As a user I want `configurationMode` to work the same way for NICs with +_**legacy**_ and _**switchdev**_ configurations. +* As a user I want to apply _**switchdev**_ configuration for NIC by the **sriov-network-operator-daemon** +without reboot (in case if reboot is not required by other logic, +e.g. kernel parameters configuration, FW configuration). + +### Goals + +* it should be possible to apply _**switchdev**_ configuration in the **sriov-network-operator-daemon** without reboot. +* the code used by `daemon` and `systemd` modes to handle _**switchdev**_ and _**legacy**_ +configurations should be the same Golang code. +* `configurationMode` option should work the same for NICs with _**legacy**_ and _**switchdev**_ configurations. +* the operator should use unified systemd services which will be deployed only +if the operator works in the `systemd` mode. +* `systemd` mode should be changed to support 2 stage configuration: +pre system network manager (NetworkManager or netplan) and after system network manager. + _Note: This is required to support all use-cases supported by the current switchdev implementation._ + + +### Non-Goals + +* replace _Externally Manage PF_ feature +* remove all shell scripts from the code + +## Proposal + +1. Drop existing bash-based implementation which is used for NICs with _**switchdev**_ configuration +2. Modify _generic_ and _virtual (if required)_ plugins to support _**switchdev**_ configuration +3. Modify code related to the _Externally Manage PF_ feature +to support _**switchdev**_ configuration +4. Modify `systemd` mode flow to handle devices with both _**legacy**_ and _**switchdev**_ configurations +5. Split `systemd` mode system service to two parts: + - `pre` - executes before NetworkManager/netplan and OVS + - `after` - executes after NetworkManager/netplan and OVS + +```mermaid +--- +title: Proposed logic +--- +flowchart TD; + operatorMode["SriovOperatorConfig.configurationMode"]; + inDaemon["apply configurations(legacy and switchdev) in + sriov-network-operator-daemon"] + systemdSystemd["create `pre` and `after` systemd services(go code) + to handle configurations(legacy and switchdev) on boot"] + + operatorMode-- daemon --> inDaemon; + operatorMode-- systemd --> systemdSystemd; + +``` + + +### Workflow Description + +Users using only NICs with _**legacy**_ SRIOV configurations will not need to change their workflow. +The operator should behave for these configurations the same way as it does now. + +Users using NICs with _**switchdev**_ configurations will need to explicitly set operator's +`configurationMode` to `systemd` if they expect the configuration of the NIC to happen +early on boot (before Kubernetes starts) to support the hwoffload use-case. + +### API Extensions + +#### SriovNetworkNodeState CR + +`SriovNetworkNodeState.status.interfaces[*].Vfs[*].vdpaType` field should be added. + +This field should be used to report information about type of the VDPA +device that is configured for VF. +Empty string means that there is no VDPA device. + +Valid values are: `virtio`, `vhost` (same as in `SriovNetworkNodePolicySpec`) + +``` +type VirtualFunction struct { + Name string `json:"name,omitempty"` + Mac string `json:"mac,omitempty"` + Assigned string `json:"assigned,omitempty"` + Driver string `json:"driver,omitempty"` + PciAddress string `json:"pciAddress"` + Vendor string `json:"vendor,omitempty"` + DeviceID string `json:"deviceID,omitempty"` + Vlan int `json:"Vlan,omitempty"` + Mtu int `json:"mtu,omitempty"` + VfID int `json:"vfID"` ++ VdpaType string `json:"vdpaType,omitempty"` +} +``` + +#### SriovOperatorConfig CR + +Change in the operator's behavior: `configurationMode` option now have effect +on NICs with _**switchdev**_ configurations. + +### Implementation Details/Notes/Constraints + +We should consider improving unit-test coverage for modified code parts during the implementation. + +After the operator upgrade, we should clean up from the host unneeded files (scripts, system services, config files) created by the previous version of the operator. + +### Upgrade & Downgrade considerations + +* after upgrading the operator, _**switchdev**_ config will be applied by **sriov-network-operator-daemon** and not by systemd service unless the user changes `configurationMode` setting to `systemd` +* after upgrading the operator, "implicit mixed mode" when _**switchdev**_ NIC configurations are handled by bash scripts(in systemd services) +and _**legacy**_ NIC configurations are managed by **sriov-network-operator-daemon** will not be supported anymore. + +_Note: `configurationMode` is a global setting, so the user will need to decide +which mode to use for the entire cluster_ + +Upgrade/Downgrade for users using only NICs with _**legacy**_ configurations will not require any actions. +Upgrade/Downgrade for clusters with _**switchdev**_ configurations will require +changing the operator's `configurationMode` option. + +### Test Plan + +The proposed changes will not introduce new functionality. + +After the refactoring, _**switchdev**_ configurations will also be supported in the `daemon` mode. +This is the only thing we may need to develop additional tests for. +All other changes should be validated by running regression testing. + +_Note: behavior for _**switchdev**_ configurations will be changed in a non-fully compatible way; +this may require to fix existing tests._ diff --git a/go.mod b/go.mod index adc5d6772..2ca75b2d9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/k8snetworkplumbingwg/sriov-network-operator -go 1.20 +go 1.21 require ( github.com/Masterminds/sprig/v3 v3.2.2 @@ -12,6 +12,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/jaypipes/ghw v0.9.0 + github.com/k8snetworkplumbingwg/govdpa v0.1.4 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/k8snetworkplumbingwg/sriov-network-device-plugin v0.0.0-20221127172732-a5a7395122e3 github.com/onsi/ginkgo/v2 v2.11.0 @@ -27,7 +28,6 @@ require ( github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae go.uber.org/zap v1.25.0 golang.org/x/time v0.3.0 - gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.28.3 k8s.io/apiextensions-apiserver v0.28.3 @@ -74,7 +74,7 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.0.0 // indirect + github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect @@ -92,7 +92,6 @@ require ( github.com/jaypipes/pcidb v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/k8snetworkplumbingwg/govdpa v0.1.4 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.17 // indirect @@ -124,22 +123,23 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20200104003542-c7e774b10ea0 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.54.0 // indirect + google.golang.org/grpc v1.56.3 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect howett.net/plist v1.0.0 // indirect k8s.io/cli-runtime v0.28.3 // indirect k8s.io/component-base v0.28.3 // indirect diff --git a/go.sum b/go.sum index 22d7d524b..092c17945 100644 --- a/go.sum +++ b/go.sum @@ -57,12 +57,14 @@ github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9 github.com/ajeddeloh/go-json v0.0.0-20170920214419-6a2fe990e083 h1:uwcvnXW76Y0rHM+qs7y8iHknWUWXYFNlD6FEVhc47TU= github.com/ajeddeloh/go-json v0.0.0-20170920214419-6a2fe990e083/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.28 h1:SaPM7dlmp7h3Lj1nJ4jdzOkTdom08+g20k7AU5heZYg= github.com/aws/aws-sdk-go v1.30.28/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beevik/etree v1.1.1-0.20200718192613-4a2f8b9d084c/go.mod h1:0yGO2rna3S9DkITDWHY1bMtcY4IJ4w+4S+EooZUR0bE= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -112,6 +114,7 @@ github.com/coreos/vcontext v0.0.0-20211021162308-f1dbbca7bef4/go.mod h1:HckqHnP/ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -131,7 +134,9 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -168,8 +173,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -302,6 +307,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -312,6 +318,7 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= @@ -343,7 +350,9 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= @@ -377,9 +386,11 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -442,6 +453,7 @@ go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -460,8 +472,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -612,13 +624,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.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= @@ -627,8 +639,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -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= @@ -783,8 +795,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -808,6 +820,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/hack/run-e2e-conformance-common b/hack/run-e2e-conformance-common new file mode 100644 index 000000000..c2a71e518 --- /dev/null +++ b/hack/run-e2e-conformance-common @@ -0,0 +1,10 @@ +#!/bin/bash + +podman_tag_and_push() { + local_image=$1 + cluster_image=$2 + + echo "## Pushing ${local_image} to cluster registry ${cluster_image}" + podman tag ${local_image} ${cluster_image} + podman push --tls-verify=false ${cluster_image} +} diff --git a/hack/run-e2e-conformance-virtual-cluster.sh b/hack/run-e2e-conformance-virtual-cluster.sh index 8c276fb4e..5d78f7a8c 100755 --- a/hack/run-e2e-conformance-virtual-cluster.sh +++ b/hack/run-e2e-conformance-virtual-cluster.sh @@ -19,6 +19,8 @@ if [ "$NUM_OF_WORKERS" -lt 2 ]; then exit 1 fi +source $here/run-e2e-conformance-common + check_requirements() { for cmd in kcli virsh virt-edit podman make go; do if ! command -v "$cmd" &> /dev/null; then @@ -166,6 +168,23 @@ method=disabled [proxy]' > /etc/NetworkManager/system-connections/multi.nmconnection chmod 600 /etc/NetworkManager/system-connections/multi.nmconnection + +echo '[Unit] +Description=disable checksum offload to avoid vf bug +After=network.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/bash -c "ethtool --offload eth1 rx off tx off && ethtool -K eth1 gso off" +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=default.target' > /etc/systemd/system/disable-offload.service + +systemctl daemon-reload +systemctl enable --now disable-offload + systemctl restart NetworkManager EOF @@ -271,8 +290,28 @@ echo "## build webhook image" podman build -t "${SRIOV_NETWORK_WEBHOOK_IMAGE}" -f "${root}/Dockerfile.webhook" "${root}" podman push --tls-verify=false "${SRIOV_NETWORK_OPERATOR_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_OPERATOR_IMAGE} podman push --tls-verify=false "${SRIOV_NETWORK_CONFIG_DAEMON_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_CONFIG_DAEMON_IMAGE} podman push --tls-verify=false "${SRIOV_NETWORK_WEBHOOK_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_WEBHOOK_IMAGE} + + +if [[ -v LOCAL_SRIOV_CNI_IMAGE ]]; then + export SRIOV_CNI_IMAGE="$controller_ip:5000/sriov-cni:latest" + podman_tag_and_push ${LOCAL_SRIOV_CNI_IMAGE} ${SRIOV_CNI_IMAGE} +fi + +if [[ -v LOCAL_SRIOV_DEVICE_PLUGIN_IMAGE ]]; then + export SRIOV_DEVICE_PLUGIN_IMAGE="$controller_ip:5000/sriov-network-device-plugin:latest" + podman_tag_and_push ${LOCAL_SRIOV_DEVICE_PLUGIN_IMAGE} ${SRIOV_DEVICE_PLUGIN_IMAGE} +fi + +if [[ -v LOCAL_NETWORK_RESOURCES_INJECTOR_IMAGE ]]; then + export NETWORK_RESOURCES_INJECTOR_IMAGE="$controller_ip:5000/network-resources-injector:latest" + podman_tag_and_push ${LOCAL_NETWORK_RESOURCES_INJECTOR_IMAGE} ${NETWORK_RESOURCES_INJECTOR_IMAGE} +fi + # remove the crio bridge and let flannel to recreate kcli ssh $cluster_name-ctlplane-0 << EOF @@ -387,9 +426,17 @@ if [ -z $SKIP_TEST ]; then export JUNIT_OUTPUT="${root}/${TEST_REPORT_PATH}/conformance-test-report" fi + # Disable exit on error temporarily to gather cluster information + set +e SUITE=./test/conformance hack/run-e2e-conformance.sh + TEST_EXITE_CODE=$? + set -e if [[ -v TEST_REPORT_PATH ]]; then kubectl cluster-info dump --namespaces ${NAMESPACE} --output-directory "${root}/${TEST_REPORT_PATH}/cluster-info" fi + + if [[ $TEST_EXITE_CODE -ne 0 ]]; then + exit $TEST_EXITE_CODE + fi fi diff --git a/hack/run-e2e-conformance-virtual-ocp.sh b/hack/run-e2e-conformance-virtual-ocp.sh index 0cd188c19..1172baa35 100755 --- a/hack/run-e2e-conformance-virtual-ocp.sh +++ b/hack/run-e2e-conformance-virtual-ocp.sh @@ -21,6 +21,8 @@ fi here="$(dirname "$(readlink --canonicalize "${BASH_SOURCE[0]}")")" root="$(readlink --canonicalize "$here/..")" +source $here/run-e2e-conformance-common + check_requirements() { for cmd in kcli virsh podman make go jq base64 tar; do if ! command -v "$cmd" &> /dev/null; then @@ -214,9 +216,31 @@ internal_registry="image-registry.openshift-image-registry.svc:5000" pass=$( jq .\"$internal_registry\".password registry-login.conf ) podman login -u serviceaccount -p ${pass:1:-1} $registry --tls-verify=false -podman push --tls-verify=false "${SRIOV_NETWORK_OPERATOR_IMAGE}" -podman push --tls-verify=false "${SRIOV_NETWORK_CONFIG_DAEMON_IMAGE}" -podman push --tls-verify=false "${SRIOV_NETWORK_WEBHOOK_IMAGE}" +MAX_RETRIES=20 +DELAY_SECONDS=10 +retry_push() { + local command="podman push --tls-verify=false $@" + local retries=0 + + until [ $retries -ge $MAX_RETRIES ]; do + $command && break + retries=$((retries+1)) + echo "Command failed. Retrying... (Attempt $retries/$MAX_RETRIES)" + sleep $DELAY_SECONDS + done + + if [ $retries -eq $MAX_RETRIES ]; then + echo "Max retries reached. Exiting..." + exit 1 + fi +} + +retry_push "${SRIOV_NETWORK_OPERATOR_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_OPERATOR_IMAGE} +retry_push "${SRIOV_NETWORK_CONFIG_DAEMON_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_CONFIG_DAEMON_IMAGE} +retry_push "${SRIOV_NETWORK_WEBHOOK_IMAGE}" +podman rmi -fi ${SRIOV_NETWORK_WEBHOOK_IMAGE} podman logout $registry @@ -241,6 +265,21 @@ export SRIOV_NETWORK_OPERATOR_IMAGE="image-registry.openshift-image-registry.svc export SRIOV_NETWORK_CONFIG_DAEMON_IMAGE="image-registry.openshift-image-registry.svc:5000/$NAMESPACE/sriov-network-config-daemon:latest" export SRIOV_NETWORK_WEBHOOK_IMAGE="image-registry.openshift-image-registry.svc:5000/$NAMESPACE/sriov-network-operator-webhook:latest" +if [[ -v LOCAL_SRIOV_CNI_IMAGE ]]; then + podman_tag_and_push ${LOCAL_SRIOV_CNI_IMAGE} "$registry/$NAMESPACE/sriov-cni:latest" + export SRIOV_CNI_IMAGE="image-registry.openshift-image-registry.svc:5000/$NAMESPACE/sriov-cni:latest" +fi + +if [[ -v LOCAL_SRIOV_DEVICE_PLUGIN_IMAGE ]]; then + podman_tag_and_push ${LOCAL_SRIOV_DEVICE_PLUGIN_IMAGE} "$registry/$NAMESPACE/sriov-network-device-plugin:latest" + export SRIOV_DEVICE_PLUGIN_IMAGE="image-registry.openshift-image-registry.svc:5000/$NAMESPACE/sriov-network-device-plugin:latest" +fi + +if [[ -v LOCAL_NETWORK_RESOURCES_INJECTOR_IMAGE ]]; then + podman_tag_and_push ${LOCAL_NETWORK_RESOURCES_INJECTOR_IMAGE} "$registry/$NAMESPACE/network-resources-injector:latest" + export NETWORK_RESOURCES_INJECTOR_IMAGE="image-registry.openshift-image-registry.svc:5000/$NAMESPACE/network-resources-injector:latest" +fi + echo "## deploying SRIOV Network Operator" hack/deploy-setup.sh $NAMESPACE @@ -254,9 +293,17 @@ if [ -z $SKIP_TEST ]; then export JUNIT_OUTPUT="${root}/${TEST_REPORT_PATH}/conformance-test-report" fi + # Disable exit on error temporarily to gather cluster information + set +e SUITE=./test/conformance hack/run-e2e-conformance.sh + TEST_EXITE_CODE=$? + set -e if [[ -v TEST_REPORT_PATH ]]; then kubectl cluster-info dump --namespaces ${NAMESPACE} --output-directory "${root}/${TEST_REPORT_PATH}/cluster-info" fi + + if [[ $TEST_EXITE_CODE -ne 0 ]]; then + exit $TEST_EXITE_CODE + fi fi diff --git a/main.go b/main.go index 7d62af130..07f3f70be 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,9 @@ import ( mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" "k8s.io/apimachinery/pkg/api/errors" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -90,12 +93,6 @@ func main() { os.Exit(1) } - openshiftContext, err := utils.NewOpenshiftContext(restConfig, scheme) - if err != nil { - setupLog.Error(err, "couldn't create openshift context") - os.Exit(1) - } - le := leaderelection.GetLeaderElectionConfig(kubeClient, enableLeaderElection) namespace := os.Getenv("NAMESPACE") @@ -137,6 +134,16 @@ func main() { os.Exit(1) } + // Initial global info + vars.Config = restConfig + vars.Scheme = mgrGlobal.GetScheme() + + platformsHelper, err := platforms.NewDefaultPlatformHelper() + if err != nil { + setupLog.Error(err, "couldn't create openshift context") + os.Exit(1) + } + if err = (&controllers.SriovNetworkReconciler{ Client: mgrGlobal.GetClient(), Scheme: mgrGlobal.GetScheme(), @@ -159,17 +166,17 @@ func main() { os.Exit(1) } if err = (&controllers.SriovOperatorConfigReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - OpenshiftContext: openshiftContext, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + PlatformHelper: platformsHelper, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SriovOperatorConfig") os.Exit(1) } if err = (&controllers.SriovNetworkPoolConfigReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - OpenshiftContext: openshiftContext, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + PlatformHelper: platformsHelper, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SriovNetworkPoolConfig") os.Exit(1) diff --git a/pkg/consts/constants.go b/pkg/consts/constants.go index 1f37edfa4..f1edc903f 100644 --- a/pkg/consts/constants.go +++ b/pkg/consts/constants.go @@ -1,8 +1,19 @@ package consts -import "time" +import ( + "fmt" + "time" +) + +type DrainState string + +// PlatformTypes +type PlatformTypes int const ( + Chroot = "/host" + Host = "/host" + ResyncPeriod = 5 * time.Minute DefaultConfigName = "default" ConfigDaemonPath = "./bindata/manifests/daemon" @@ -19,6 +30,8 @@ const ( DefaultPolicyName = "default" ConfigMapName = "device-plugin-config" DaemonSet = "DaemonSet" + Role = "Role" + RoleBinding = "RoleBinding" ServiceAccount = "ServiceAccount" DPConfigFileName = "config.json" OVSHWOLMachineConfigNameSuffix = "ovs-hw-offload" @@ -33,4 +46,86 @@ const ( DeviceTypeNetDevice = "netdevice" VdpaTypeVirtio = "virtio" VdpaTypeVhost = "vhost" + + ClusterTypeOpenshift = "openshift" + ClusterTypeKubernetes = "kubernetes" + + SriovConfBasePath = "/etc/sriov-operator" + PfAppliedConfig = SriovConfBasePath + "/pci" + SriovSwitchDevConfPath = SriovConfBasePath + "/sriov_config.json" + SriovHostSwitchDevConfPath = Host + SriovSwitchDevConfPath + + DrainAnnotationState = "sriovnetwork.openshift.io/state" + DrainAnnotationStateRequired = "sriovnetwork.openshift.io/state-required" + DrainAnnotationTime = "sriovnetwork.openshift.io/state-time" + + DrainIdle DrainState = "Idle" + DrainDisabled DrainState = "Drain_Disabled" + DrainRequired DrainState = "Drain_Required" + RebootRequired DrainState = "Reboot_Required" + DrainMcpPausing DrainState = "Draining_MCP_Pausing" + DrainMcpPaused DrainState = "Draining_MCP_Paused" + Draining DrainState = "Draining" + DrainingComplete DrainState = "Draining_Complete" + RebootComplete DrainState = "Reboot_Complete" + + SyncStatusSucceeded = "Succeeded" + SyncStatusFailed = "Failed" + SyncStatusInProgress = "InProgress" + + MCPPauseAnnotationState = "sriovnetwork.openshift.io/state" + MCPPauseAnnotationTime = "sriovnetwork.openshift.io/time" + + CheckpointFileName = "sno-initial-node-state.json" + Unknown = "Unknown" + + SysBus = "/sys/bus" + SysBusPciDevices = SysBus + "/pci/devices" + SysBusPciDrivers = SysBus + "/pci/drivers" + SysBusPciDriversProbe = SysBus + "/pci/drivers_probe" + SysClassNet = "/sys/class/net" + ProcKernelCmdLine = "/proc/cmdline" + NetClass = 0x02 + NumVfsFile = "sriov_numvfs" + BusPci = "pci" + BusVdpa = "vdpa" + + UdevFolder = "/etc/udev" + UdevRulesFolder = UdevFolder + "/rules.d" + HostUdevRulesFolder = Host + UdevRulesFolder + UdevDisableNM = "/bindata/scripts/udev-find-sriov-pf.sh" + // nolint:goconst + NMUdevRule = `SUBSYSTEM=="net", ` + + `ACTION=="add|change|move", ` + + `ATTRS{device}=="%s", ` + + `IMPORT{program}="/etc/udev/disable-nm-sriov.sh $env{INTERFACE} %s"` + // nolint:goconst + SwitchdevUdevRule = `SUBSYSTEM=="net", ` + + `ACTION=="add|move", ` + + `ATTRS{phys_switch_id}=="%s", ` + + `ATTR{phys_port_name}=="pf%svf*", ` + + `IMPORT{program}="/etc/udev/switchdev-vf-link-name.sh $attr{phys_port_name}", ` + + `NAME="%s_$env{NUMBER}"` + + KernelArgPciRealloc = "pci=realloc" + KernelArgIntelIommu = "intel_iommu=on" + KernelArgIommuPt = "iommu=pt" ) + +const ( + // Baremetal platform + Baremetal PlatformTypes = iota + // VirtualOpenStack platform + VirtualOpenStack +) + +func (e PlatformTypes) String() string { + switch e { + case Baremetal: + return "Baremetal" + case VirtualOpenStack: + return "Virtual/Openstack" + default: + return fmt.Sprintf("%d", int(e)) + } +} diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 913cad805..c6c845cb0 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -39,12 +39,13 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" sninformer "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/informers/externalversions" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/service" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/systemd" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) const ( @@ -62,26 +63,17 @@ type Message struct { } type Daemon struct { - // name is the node name. - name string - - platform utils.PlatformType - - useSystemdService bool - - devMode bool - client snclientset.Interface // kubeClient allows interaction with Kubernetes, including the node we are running on. kubeClient kubernetes.Interface - openshiftContext *utils.OpenshiftContext - nodeState *sriovnetworkv1.SriovNetworkNodeState enabledPlugins map[string]plugin.VendorPlugin - serviceManager service.ServiceManager + HostHelpers helper.HostHelpersInterface + + platformHelpers platforms.Interface // channel used by callbacks to signal Run() of an error exitCh chan<- error @@ -109,26 +101,17 @@ type Daemon struct { mcpName string - storeManager utils.StoreManagerInterface - - hostManager host.HostManagerInterface - eventRecorder *EventRecorder } const ( - udevScriptsPath = "/bindata/scripts/load-udev.sh" - annoKey = "sriovnetwork.openshift.io/state" - annoIdle = "Idle" - annoDraining = "Draining" - annoMcpPaused = "Draining_MCP_Paused" - syncStatusSucceeded = "Succeeded" - syncStatusFailed = "Failed" - syncStatusInProgress = "InProgress" + udevScriptsPath = "/bindata/scripts/load-udev.sh" + annoKey = "sriovnetwork.openshift.io/state" + annoIdle = "Idle" + annoDraining = "Draining" + annoMcpPaused = "Draining_MCP_Paused" ) -var namespace = os.Getenv("NAMESPACE") - // writer implements io.Writer interface as a pass-through for log.Log. type writer struct { logFunc func(msg string, keysAndValues ...interface{}) @@ -141,33 +124,26 @@ func (w writer) Write(p []byte) (n int, err error) { } func New( - nodeName string, client snclientset.Interface, kubeClient kubernetes.Interface, - openshiftContext *utils.OpenshiftContext, + hostHelpers helper.HostHelpersInterface, + platformHelper platforms.Interface, exitCh chan<- error, stopCh <-chan struct{}, syncCh <-chan struct{}, refreshCh chan<- Message, - platformType utils.PlatformType, - useSystemdService bool, er *EventRecorder, - devMode bool, ) *Daemon { return &Daemon{ - name: nodeName, - platform: platformType, - useSystemdService: useSystemdService, - devMode: devMode, - client: client, - kubeClient: kubeClient, - openshiftContext: openshiftContext, - serviceManager: service.NewServiceManager("/host"), - exitCh: exitCh, - stopCh: stopCh, - syncCh: syncCh, - refreshCh: refreshCh, - nodeState: &sriovnetworkv1.SriovNetworkNodeState{}, + client: client, + kubeClient: kubeClient, + HostHelpers: hostHelpers, + platformHelpers: platformHelper, + exitCh: exitCh, + stopCh: stopCh, + syncCh: syncCh, + refreshCh: refreshCh, + nodeState: &sriovnetworkv1.SriovNetworkNodeState{}, drainer: &drain.Helper{ Client: kubeClient, Force: true, @@ -195,38 +171,30 @@ func New( // Run the config daemon func (dn *Daemon) Run(stopCh <-chan struct{}, exitCh <-chan error) error { - log.Log.V(0).Info("Run()", "node", dn.name) + log.Log.V(0).Info("Run()", "node", vars.NodeName) - if utils.ClusterType == utils.ClusterTypeOpenshift { - log.Log.V(0).Info("Run(): start daemon.", "openshiftFlavor", dn.openshiftContext.OpenshiftFlavor) + if vars.ClusterType == consts.ClusterTypeOpenshift { + log.Log.V(0).Info("Run(): start daemon.", "openshiftFlavor", dn.platformHelpers.GetFlavor()) } else { log.Log.V(0).Info("Run(): start daemon.") } - if dn.useSystemdService { - log.Log.V(0).Info("Run(): daemon running in systemd mode") - } - // Only watch own SriovNetworkNodeState CR - defer utilruntime.HandleCrash() - defer dn.workqueue.ShutDown() - - hostManager := host.NewHostManager(dn.useSystemdService) - dn.hostManager = hostManager - if !dn.useSystemdService { - dn.hostManager.TryEnableRdma() - dn.hostManager.TryEnableTun() - dn.hostManager.TryEnableVhostNet() - err := systemd.CleanSriovFilesFromHost(utils.ClusterType == utils.ClusterTypeOpenshift) + if !vars.UsingSystemdMode { + log.Log.V(0).Info("Run(): daemon running in daemon mode") + dn.HostHelpers.TryEnableRdma() + dn.HostHelpers.TryEnableTun() + dn.HostHelpers.TryEnableVhostNet() + err := systemd.CleanSriovFilesFromHost(vars.ClusterType == consts.ClusterTypeOpenshift) if err != nil { log.Log.Error(err, "failed to remove all the systemd sriov files") } + } else { + log.Log.V(0).Info("Run(): daemon running in systemd mode") } - storeManager, err := utils.NewStoreManager(false) - if err != nil { - return err - } - dn.storeManager = storeManager + // Only watch own SriovNetworkNodeState CR + defer utilruntime.HandleCrash() + defer dn.workqueue.ShutDown() if err := dn.prepareNMUdevRule(); err != nil { log.Log.Error(err, "failed to prepare udev files to disable network manager on requested VFs") @@ -236,12 +204,13 @@ func (dn *Daemon) Run(stopCh <-chan struct{}, exitCh <-chan error) error { } var timeout int64 = 5 + var metadataKey = "metadata.name" dn.mu = &sync.Mutex{} informerFactory := sninformer.NewFilteredSharedInformerFactory(dn.client, time.Second*15, - namespace, + vars.Namespace, func(lo *metav1.ListOptions) { - lo.FieldSelector = "metadata.name=" + dn.name + lo.FieldSelector = metadataKey + "=" + vars.NodeName lo.TimeoutSeconds = &timeout }, ) @@ -256,9 +225,9 @@ func (dn *Daemon) Run(stopCh <-chan struct{}, exitCh <-chan error) error { cfgInformerFactory := sninformer.NewFilteredSharedInformerFactory(dn.client, time.Second*30, - namespace, + vars.Namespace, func(lo *metav1.ListOptions) { - lo.FieldSelector = "metadata.name=" + "default" + lo.FieldSelector = metadataKey + "=" + "default" }, ) @@ -300,7 +269,7 @@ func (dn *Daemon) Run(stopCh <-chan struct{}, exitCh <-chan error) error { log.Log.Error(err, "got an error") if more { dn.refreshCh <- Message{ - syncStatus: syncStatusFailed, + syncStatus: consts.SyncStatusFailed, lastSyncError: err.Error(), } } @@ -358,7 +327,7 @@ func (dn *Daemon) processNextWorkItem() bool { if err != nil { // Ereport error message, and put the item back to work queue for retry. dn.refreshCh <- Message{ - syncStatus: syncStatusFailed, + syncStatus: consts.SyncStatusFailed, lastSyncError: err.Error(), } <-dn.syncCh @@ -384,9 +353,9 @@ func (dn *Daemon) nodeAddHandler(obj interface{}) { } func (dn *Daemon) nodeUpdateHandler(old, new interface{}) { - node, err := dn.nodeLister.Get(dn.name) + node, err := dn.nodeLister.Get(vars.NodeName) if errors.IsNotFound(err) { - log.Log.V(2).Info("nodeUpdateHandler(): node has been deleted", "name", dn.name) + log.Log.V(2).Info("nodeUpdateHandler(): node has been deleted", "name", vars.NodeName) return } dn.node = node.DeepCopy() @@ -399,7 +368,7 @@ func (dn *Daemon) nodeUpdateHandler(old, new interface{}) { // Checking if other nodes are draining for _, otherNode := range nodes { - if otherNode.GetName() == dn.name { + if otherNode.GetName() == vars.NodeName { continue } @@ -438,24 +407,24 @@ func (dn *Daemon) nodeStateSyncHandler() error { var err error // Get the latest NodeState var latestState *sriovnetworkv1.SriovNetworkNodeState - var sriovResult = &systemd.SriovResult{SyncStatus: syncStatusSucceeded, LastSyncError: ""} - latestState, err = dn.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).Get(context.Background(), dn.name, metav1.GetOptions{}) + var sriovResult = &systemd.SriovResult{SyncStatus: consts.SyncStatusSucceeded, LastSyncError: ""} + latestState, err = dn.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).Get(context.Background(), vars.NodeName, metav1.GetOptions{}) if err != nil { - log.Log.Error(err, "nodeStateSyncHandler(): Failed to fetch node state", "name", dn.name) + log.Log.Error(err, "nodeStateSyncHandler(): Failed to fetch node state", "name", vars.NodeName) return err } latest := latestState.GetGeneration() log.Log.V(0).Info("nodeStateSyncHandler(): new generation", "generation", latest) - if utils.ClusterType == utils.ClusterTypeOpenshift && !dn.openshiftContext.IsHypershift() { + if vars.ClusterType == consts.ClusterTypeOpenshift && !dn.platformHelpers.IsHypershift() { if err = dn.getNodeMachinePool(); err != nil { return err } } if dn.nodeState.GetGeneration() == latest { - if dn.useSystemdService { - serviceEnabled, err := dn.serviceManager.IsServiceEnabled(systemd.SriovServicePath) + if vars.UsingSystemdMode { + serviceEnabled, err := dn.HostHelpers.IsServiceEnabled(systemd.SriovServicePath) if err != nil { log.Log.Error(err, "nodeStateSyncHandler(): failed to check if sriov-config service exist on host") return err @@ -465,7 +434,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { // this is only for k8s base environments, for openshift the sriov-operator creates a machine config to will apply // the system service and reboot the node the config-daemon doesn't need to do anything. if !serviceEnabled { - sriovResult = &systemd.SriovResult{SyncStatus: syncStatusFailed, + sriovResult = &systemd.SriovResult{SyncStatus: consts.SyncStatusFailed, LastSyncError: "sriov-config systemd service is not available on node"} } else { sriovResult, err = systemd.ReadSriovResult() @@ -474,12 +443,12 @@ func (dn *Daemon) nodeStateSyncHandler() error { return err } } - if sriovResult.LastSyncError != "" || sriovResult.SyncStatus == syncStatusFailed { + if sriovResult.LastSyncError != "" || sriovResult.SyncStatus == consts.SyncStatusFailed { log.Log.Info("nodeStateSyncHandler(): sync failed systemd service error", "last-sync-error", sriovResult.LastSyncError) // add the error but don't requeue dn.refreshCh <- Message{ - syncStatus: syncStatusFailed, + syncStatus: consts.SyncStatusFailed, lastSyncError: sriovResult.LastSyncError, } <-dn.syncCh @@ -488,9 +457,9 @@ func (dn *Daemon) nodeStateSyncHandler() error { } log.Log.V(0).Info("nodeStateSyncHandler(): Interface not changed") if latestState.Status.LastSyncError != "" || - latestState.Status.SyncStatus != syncStatusSucceeded { + latestState.Status.SyncStatus != consts.SyncStatusSucceeded { dn.refreshCh <- Message{ - syncStatus: syncStatusSucceeded, + syncStatus: consts.SyncStatusSucceeded, lastSyncError: "", } // wait for writer to refresh the status @@ -501,7 +470,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } if latestState.GetGeneration() == 1 && len(latestState.Spec.Interfaces) == 0 { - err = dn.storeManager.ClearPCIAddressFolder() + err = dn.HostHelpers.ClearPCIAddressFolder() if err != nil { log.Log.Error(err, "failed to clear the PCI address configuration") return err @@ -522,7 +491,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } dn.refreshCh <- Message{ - syncStatus: syncStatusInProgress, + syncStatus: consts.SyncStatusInProgress, lastSyncError: "", } // wait for writer to refresh status then pull again the latest node state @@ -531,16 +500,16 @@ func (dn *Daemon) nodeStateSyncHandler() error { // we need to load the latest status to our object // if we don't do it we can have a race here where the user remove the virtual functions but the operator didn't // trigger the refresh - updatedState, err := dn.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).Get(context.Background(), dn.name, metav1.GetOptions{}) + updatedState, err := dn.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).Get(context.Background(), vars.NodeName, metav1.GetOptions{}) if err != nil { - log.Log.Error(err, "nodeStateSyncHandler(): Failed to fetch node state", "name", dn.name) + log.Log.Error(err, "nodeStateSyncHandler(): Failed to fetch node state", "name", vars.NodeName) return err } latestState.Status = updatedState.Status // load plugins if it has not loaded if len(dn.enabledPlugins) == 0 { - dn.enabledPlugins, err = enablePlugins(dn.platform, dn.useSystemdService, latestState, dn.hostManager, dn.storeManager) + dn.enabledPlugins, err = enablePlugins(latestState, dn.HostHelpers) if err != nil { log.Log.Error(err, "nodeStateSyncHandler(): failed to enable vendor plugins") return err @@ -571,8 +540,9 @@ func (dn *Daemon) nodeStateSyncHandler() error { // When running using systemd check if the applied configuration is the latest one // or there is a new config we need to apply // When using systemd configuration we write the file - if dn.useSystemdService { - systemdConfModified, err := systemd.WriteConfFile(latestState, dn.devMode, dn.platform) + if vars.UsingSystemdMode { + log.Log.V(0).Info("nodeStateSyncHandler(): writing systemd config file to host") + systemdConfModified, err := systemd.WriteConfFile(latestState) if err != nil { log.Log.Error(err, "nodeStateSyncHandler(): failed to write configuration file for systemd mode") return err @@ -610,7 +580,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } } } - if dn.openshiftContext.IsOpenshiftCluster() && !dn.openshiftContext.IsHypershift() { + if dn.platformHelpers.IsOpenshiftCluster() && !dn.platformHelpers.IsHypershift() { if err = dn.getNodeMachinePool(); err != nil { return err } @@ -628,7 +598,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } } - if dn.openshiftContext.IsOpenshiftCluster() && !dn.openshiftContext.IsHypershift() { + if dn.platformHelpers.IsOpenshiftCluster() && !dn.platformHelpers.IsHypershift() { log.Log.Info("nodeStateSyncHandler(): pause MCP") if err := dn.pauseMCP(); err != nil { return err @@ -645,7 +615,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } } - if !reqReboot && !dn.useSystemdService { + if !reqReboot && !vars.UsingSystemdMode { // For BareMetal machines apply the generic plugin selectedPlugin, ok := dn.enabledPlugins[GenericPluginName] if ok { @@ -672,7 +642,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { if reqReboot { log.Log.Info("nodeStateSyncHandler(): reboot node") dn.eventRecorder.SendEvent("RebootNode", "Reboot node has been initiated") - rebootNode() + dn.rebootNode() return nil } @@ -689,7 +659,7 @@ func (dn *Daemon) nodeStateSyncHandler() error { } } else { if !dn.nodeHasAnnotation(annoKey, annoIdle) { - if err := dn.annotateNode(dn.name, annoIdle); err != nil { + if err := dn.annotateNode(vars.NodeName, annoIdle); err != nil { log.Log.Error(err, "nodeStateSyncHandler(): failed to annotate node") return err } @@ -697,14 +667,14 @@ func (dn *Daemon) nodeStateSyncHandler() error { } log.Log.Info("nodeStateSyncHandler(): sync succeeded") dn.nodeState = latestState.DeepCopy() - if dn.useSystemdService { + if vars.UsingSystemdMode { dn.refreshCh <- Message{ syncStatus: sriovResult.SyncStatus, lastSyncError: sriovResult.LastSyncError, } } else { dn.refreshCh <- Message{ - syncStatus: syncStatusSucceeded, + syncStatus: consts.SyncStatusSucceeded, lastSyncError: "", } } @@ -739,16 +709,16 @@ func (dn *Daemon) completeDrain() error { } } - if dn.openshiftContext.IsOpenshiftCluster() && !dn.openshiftContext.IsHypershift() { + if dn.platformHelpers.IsOpenshiftCluster() && !dn.platformHelpers.IsHypershift() { log.Log.Info("completeDrain(): resume MCP", "mcp-name", dn.mcpName) pausePatch := []byte("{\"spec\":{\"paused\":false}}") - if _, err := dn.openshiftContext.McClient.MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}); err != nil { + if _, err := dn.platformHelpers.GetMcClient().MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}); err != nil { log.Log.Error(err, "completeDrain(): failed to resume MCP", "mcp-name", dn.mcpName) return err } } - if err := dn.annotateNode(dn.name, annoIdle); err != nil { + if err := dn.annotateNode(vars.NodeName, annoIdle); err != nil { log.Log.Error(err, "completeDrain(): failed to annotate node") return err } @@ -761,9 +731,9 @@ func (dn *Daemon) restartDevicePluginPod() error { log.Log.V(2).Info("restartDevicePluginPod(): try to restart device plugin pod") var podToDelete string - pods, err := dn.kubeClient.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ + pods, err := dn.kubeClient.CoreV1().Pods(vars.Namespace).List(context.Background(), metav1.ListOptions{ LabelSelector: "app=sriov-device-plugin", - FieldSelector: "spec.nodeName=" + dn.name, + FieldSelector: "spec.nodeName=" + vars.NodeName, }) if err != nil { if errors.IsNotFound(err) { @@ -781,7 +751,7 @@ func (dn *Daemon) restartDevicePluginPod() error { podToDelete = pods.Items[0].Name log.Log.V(2).Info("restartDevicePluginPod(): Found device plugin pod, deleting it", "pod-name", podToDelete) - err = dn.kubeClient.CoreV1().Pods(namespace).Delete(context.Background(), podToDelete, metav1.DeleteOptions{}) + err = dn.kubeClient.CoreV1().Pods(vars.Namespace).Delete(context.Background(), podToDelete, metav1.DeleteOptions{}) if errors.IsNotFound(err) { log.Log.Info("restartDevicePluginPod(): pod to delete not found") return nil @@ -792,7 +762,7 @@ func (dn *Daemon) restartDevicePluginPod() error { } if err := wait.PollImmediateUntil(3*time.Second, func() (bool, error) { - _, err := dn.kubeClient.CoreV1().Pods(namespace).Get(context.Background(), podToDelete, metav1.GetOptions{}) + _, err := dn.kubeClient.CoreV1().Pods(vars.Namespace).Get(context.Background(), podToDelete, metav1.GetOptions{}) if errors.IsNotFound(err) { log.Log.Info("restartDevicePluginPod(): device plugin pod exited") return true, nil @@ -812,9 +782,9 @@ func (dn *Daemon) restartDevicePluginPod() error { return nil } -func rebootNode() { +func (dn *Daemon) rebootNode() { log.Log.Info("rebootNode(): trigger node reboot") - exit, err := utils.Chroot("/host") + exit, err := dn.HostHelpers.Chroot(consts.Host) if err != nil { log.Log.Error(err, "rebootNode(): chroot command failed") } @@ -837,7 +807,7 @@ func rebootNode() { func (dn *Daemon) annotateNode(node, value string) error { log.Log.Info("annotateNode(): Annotate node", "name", node, "value", value) - oldNode, err := dn.kubeClient.CoreV1().Nodes().Get(context.Background(), dn.name, metav1.GetOptions{}) + oldNode, err := dn.kubeClient.CoreV1().Nodes().Get(context.Background(), vars.NodeName, metav1.GetOptions{}) if err != nil { log.Log.Error(err, "annotateNode(): Failed to get node, retrying", "name", node) return err @@ -863,7 +833,7 @@ func (dn *Daemon) annotateNode(node, value string) error { return err } _, err = dn.kubeClient.CoreV1().Nodes().Patch(context.Background(), - dn.name, + vars.NodeName, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) @@ -881,7 +851,7 @@ func (dn *Daemon) getNodeMachinePool() error { log.Log.Error(nil, "getNodeMachinePool(): Failed to find the the desiredConfig Annotation") return fmt.Errorf("getNodeMachinePool(): Failed to find the the desiredConfig Annotation") } - mc, err := dn.openshiftContext.McClient.MachineconfigurationV1().MachineConfigs().Get(context.TODO(), desiredConfig, metav1.GetOptions{}) + mc, err := dn.platformHelpers.GetMcClient().MachineconfigurationV1().MachineConfigs().Get(context.TODO(), desiredConfig, metav1.GetOptions{}) if err != nil { log.Log.Error(err, "getNodeMachinePool(): Failed to get the desired Machine Config") return err @@ -903,11 +873,11 @@ func (dn *Daemon) getDrainLock(ctx context.Context, done chan bool) { lock := &resourcelock.LeaseLock{ LeaseMeta: metav1.ObjectMeta{ Name: "config-daemon-draining-lock", - Namespace: namespace, + Namespace: vars.Namespace, }, Client: dn.kubeClient.CoordinationV1(), LockConfig: resourcelock.ResourceLockConfig{ - Identity: dn.name, + Identity: vars.NodeName, }, } @@ -930,7 +900,7 @@ func (dn *Daemon) getDrainLock(ctx context.Context, done chan bool) { } if dn.drainable { log.Log.V(2).Info("getDrainLock(): no other node is draining") - err = dn.annotateNode(dn.name, annoDraining) + err = dn.annotateNode(vars.NodeName, annoDraining) if err != nil { log.Log.Error(err, "getDrainLock(): failed to annotate node") continue @@ -952,7 +922,7 @@ func (dn *Daemon) pauseMCP() error { log.Log.Info("pauseMCP(): pausing MCP") var err error - mcpInformerFactory := mcfginformers.NewSharedInformerFactory(dn.openshiftContext.McClient, + mcpInformerFactory := mcfginformers.NewSharedInformerFactory(dn.platformHelpers.GetMcClient(), time.Second*30, ) mcpInformer := mcpInformerFactory.Machineconfiguration().V1().MachineConfigPools().Informer() @@ -967,7 +937,7 @@ func (dn *Daemon) pauseMCP() error { return } // Always get the latest object - newMcp, err := dn.openshiftContext.McClient.MachineconfigurationV1().MachineConfigPools().Get(ctx, dn.mcpName, metav1.GetOptions{}) + newMcp, err := dn.platformHelpers.GetMcClient().MachineconfigurationV1().MachineConfigPools().Get(ctx, dn.mcpName, metav1.GetOptions{}) if err != nil { log.Log.V(2).Error(err, "pauseMCP(): Failed to get MCP", "mcp-name", dn.mcpName) return @@ -987,12 +957,12 @@ func (dn *Daemon) pauseMCP() error { } log.Log.Info("pauseMCP(): pause MCP", "mcp-name", dn.mcpName) pausePatch := []byte("{\"spec\":{\"paused\":true}}") - _, err = dn.openshiftContext.McClient.MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}) + _, err = dn.platformHelpers.GetMcClient().MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}) if err != nil { log.Log.V(2).Error(err, "pauseMCP(): failed to pause MCP", "mcp-name", dn.mcpName) return } - err = dn.annotateNode(dn.name, annoMcpPaused) + err = dn.annotateNode(vars.NodeName, annoMcpPaused) if err != nil { log.Log.V(2).Error(err, "pauseMCP(): Failed to annotate node") return @@ -1003,12 +973,12 @@ func (dn *Daemon) pauseMCP() error { if paused { log.Log.Info("pauseMCP(): MCP is processing, resume MCP", "mcp-name", dn.mcpName) pausePatch := []byte("{\"spec\":{\"paused\":false}}") - _, err = dn.openshiftContext.McClient.MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}) + _, err = dn.platformHelpers.GetMcClient().MachineconfigurationV1().MachineConfigPools().Patch(context.Background(), dn.mcpName, types.MergePatchType, pausePatch, metav1.PatchOptions{}) if err != nil { log.Log.V(2).Error(err, "pauseMCP(): fail to resume MCP", "mcp-name", dn.mcpName) return } - err = dn.annotateNode(dn.name, annoDraining) + err = dn.annotateNode(vars.NodeName, annoDraining) if err != nil { log.Log.V(2).Error(err, "pauseMCP(): Failed to annotate node") return @@ -1057,7 +1027,7 @@ func (dn *Daemon) drainNode() error { log.Log.Error(err, "cordon failed, retrying") return false, nil } - err = drain.RunNodeDrain(dn.drainer, dn.name) + err = drain.RunNodeDrain(dn.drainer, vars.NodeName) if err == nil { return true, nil } @@ -1077,28 +1047,29 @@ func (dn *Daemon) drainNode() error { return nil } +// TODO: move this to host interface func (dn *Daemon) tryCreateSwitchdevUdevRule() error { log.Log.V(2).Info("tryCreateSwitchdevUdevRule()") - nodeState, nodeStateErr := dn.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).Get( + nodeState, nodeStateErr := dn.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).Get( context.Background(), - dn.name, + vars.NodeName, metav1.GetOptions{}, ) if nodeStateErr != nil { - log.Log.Error(nodeStateErr, "could not fetch node state, skip updating switchdev udev rules", "name", dn.name) + log.Log.Error(nodeStateErr, "could not fetch node state, skip updating switchdev udev rules", "name", vars.NodeName) return nil } var newContent string - filePath := path.Join(utils.FilesystemRoot, "/host/etc/udev/rules.d/20-switchdev.rules") + filePath := path.Join(vars.FilesystemRoot, "/host/etc/udev/rules.d/20-switchdev.rules") for _, ifaceStatus := range nodeState.Status.Interfaces { if ifaceStatus.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { - switchID, err := utils.GetPhysSwitchID(ifaceStatus.Name) + switchID, err := dn.HostHelpers.GetPhysSwitchID(ifaceStatus.Name) if err != nil { return err } - portName, err := utils.GetPhysPortName(ifaceStatus.Name) + portName, err := dn.HostHelpers.GetPhysPortName(ifaceStatus.Name) if err != nil { return err } @@ -1126,7 +1097,7 @@ func (dn *Daemon) tryCreateSwitchdevUdevRule() error { } var stdout, stderr bytes.Buffer - cmd := exec.Command("/bin/bash", path.Join(utils.FilesystemRoot, udevScriptsPath)) + cmd := exec.Command("/bin/bash", path.Join(vars.FilesystemRoot, udevScriptsPath)) cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { @@ -1158,5 +1129,5 @@ func (dn *Daemon) prepareNMUdevRule() error { supportedVfIds = append(supportedVfIds, vfID) } - return utils.PrepareNMUdevRule(supportedVfIds) + return dn.HostHelpers.PrepareNMUdevRule(supportedVfIds) } diff --git a/pkg/daemon/daemon_test.go b/pkg/daemon/daemon_test.go index 5300a1a65..e81f7b677 100644 --- a/pkg/daemon/daemon_test.go +++ b/pkg/daemon/daemon_test.go @@ -5,6 +5,7 @@ import ( "flag" "testing" + "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -12,20 +13,24 @@ import ( fakek8s "k8s.io/client-go/kubernetes/fake" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + mock_platforms "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/mock" "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" fakesnclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned/fake" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + mock_helper "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper/mock" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/fake" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/generic" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) var FakeSupportedNicIDs corev1.ConfigMap = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: sriovnetworkv1.SupportedNicIDConfigmap, - Namespace: namespace, + Namespace: vars.Namespace, }, Data: map[string]string{ "Intel_i40e_XXV710": "8086 158a 154c", @@ -36,7 +41,7 @@ var FakeSupportedNicIDs corev1.ConfigMap = corev1.ConfigMap{ var SriovDevicePluginPod corev1.Pod = corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "sriov-device-plugin-xxxx", - Namespace: namespace, + Namespace: vars.Namespace, Labels: map[string]string{ "app": "sriov-device-plugin", }, @@ -95,29 +100,44 @@ var _ = Describe("Config Daemon", func() { } var err error - utils.FilesystemRoot, cleanFakeFs, err = fakeFs.Use() + vars.FilesystemRoot, cleanFakeFs, err = fakeFs.Use() Expect(err).ToNot(HaveOccurred()) + vars.UsingSystemdMode = false + vars.NodeName = "test-node" + vars.PlatformType = consts.Baremetal + kubeClient := fakek8s.NewSimpleClientset(&FakeSupportedNicIDs, &SriovDevicePluginPod) client := fakesnclientset.NewSimpleClientset() - err = sriovnetworkv1.InitNicIDMapFromConfigMap(kubeClient, namespace) + err = sriovnetworkv1.InitNicIDMapFromConfigMap(kubeClient, vars.Namespace) Expect(err).ToNot(HaveOccurred()) - er := NewEventRecorder(client, "test-node", kubeClient) + er := NewEventRecorder(client, kubeClient) + + t := GinkgoT() + mockCtrl := gomock.NewController(t) + platformHelper := mock_platforms.NewMockInterface(mockCtrl) + platformHelper.EXPECT().GetFlavor().Return(openshift.OpenshiftFlavorDefault).AnyTimes() + platformHelper.EXPECT().IsOpenshiftCluster().Return(false).AnyTimes() + platformHelper.EXPECT().IsHypershift().Return(false).AnyTimes() - sut = New("test-node", + vendorHelper := mock_helper.NewMockHostHelpersInterface(mockCtrl) + vendorHelper.EXPECT().TryEnableRdma().Return(true, nil).AnyTimes() + vendorHelper.EXPECT().TryEnableVhostNet().AnyTimes() + vendorHelper.EXPECT().TryEnableTun().AnyTimes() + vendorHelper.EXPECT().PrepareNMUdevRule([]string{"0x1014", "0x154c"}).Return(nil).AnyTimes() + + sut = New( client, kubeClient, - &utils.OpenshiftContext{IsOpenShiftCluster: false, OpenshiftFlavor: ""}, + vendorHelper, + platformHelper, exitCh, stopCh, syncCh, refreshCh, - utils.Baremetal, - false, er, - false, ) sut.enabledPlugins = map[string]plugin.VendorPlugin{generic.PluginName: &fake.FakePlugin{}} @@ -183,7 +203,7 @@ var _ = Describe("Config Daemon", func() { Expect(msg.syncStatus).To(Equal("Succeeded")) Eventually(func() (int, error) { - podList, err := sut.kubeClient.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ + podList, err := sut.kubeClient.CoreV1().Pods(vars.Namespace).List(context.Background(), metav1.ListOptions{ LabelSelector: "app=sriov-device-plugin", FieldSelector: "spec.nodeName=test-node", }) @@ -236,78 +256,18 @@ var _ = Describe("Config Daemon", func() { Expect(sut.nodeState.GetGeneration()).To(BeNumerically("==", 777)) }) }) - - Context("isNodeDraining", func() { - - It("for a non-Openshift cluster", func() { - sut.openshiftContext = &utils.OpenshiftContext{IsOpenShiftCluster: false} - - sut.node = &corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-node", - Annotations: map[string]string{}}} - - Expect(sut.isNodeDraining()).To(BeFalse()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining" - Expect(sut.isNodeDraining()).To(BeTrue()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining_MCP_Paused" - Expect(sut.isNodeDraining()).To(BeTrue()) - }) - - It("for an Openshift cluster", func() { - sut.openshiftContext = &utils.OpenshiftContext{ - IsOpenShiftCluster: true, - OpenshiftFlavor: utils.OpenshiftFlavorDefault, - } - - sut.node = &corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-node", - Annotations: map[string]string{}}} - - Expect(sut.isNodeDraining()).To(BeFalse()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining" - Expect(sut.isNodeDraining()).To(BeTrue()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining_MCP_Paused" - Expect(sut.isNodeDraining()).To(BeTrue()) - }) - - It("for an Openshift Hypershift cluster", func() { - sut.openshiftContext = &utils.OpenshiftContext{ - IsOpenShiftCluster: true, - OpenshiftFlavor: utils.OpenshiftFlavorHypershift, - } - - sut.node = &corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-node", - Annotations: map[string]string{}}} - - Expect(sut.isNodeDraining()).To(BeFalse()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining" - Expect(sut.isNodeDraining()).To(BeTrue()) - - sut.node.Annotations["sriovnetwork.openshift.io/state"] = "Draining_MCP_Paused" - Expect(sut.isNodeDraining()).To(BeTrue()) - }) - }) }) func createSriovNetworkNodeState(c snclientset.Interface, nodeState *sriovnetworkv1.SriovNetworkNodeState) error { _, err := c.SriovnetworkV1(). - SriovNetworkNodeStates(namespace). + SriovNetworkNodeStates(vars.Namespace). Create(context.Background(), nodeState, metav1.CreateOptions{}) return err } func updateSriovNetworkNodeState(c snclientset.Interface, nodeState *sriovnetworkv1.SriovNetworkNodeState) error { _, err := c.SriovnetworkV1(). - SriovNetworkNodeStates(namespace). + SriovNetworkNodeStates(vars.Namespace). Update(context.Background(), nodeState, metav1.UpdateOptions{}) return err } diff --git a/pkg/daemon/event_recorder.go b/pkg/daemon/event_recorder.go index 2860cf84a..25b2d2351 100644 --- a/pkg/daemon/event_recorder.go +++ b/pkg/daemon/event_recorder.go @@ -12,24 +12,23 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) type EventRecorder struct { client snclientset.Interface - node string eventRecorder record.EventRecorder eventBroadcaster record.EventBroadcaster } // NewEventRecorder Create a new EventRecorder -func NewEventRecorder(c snclientset.Interface, n string, kubeclient kubernetes.Interface) *EventRecorder { +func NewEventRecorder(c snclientset.Interface, kubeclient kubernetes.Interface) *EventRecorder { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartStructuredLogging(4) eventBroadcaster.StartRecordingToSink(&typedv1core.EventSinkImpl{Interface: kubeclient.CoreV1().Events("")}) eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "config-daemon"}) return &EventRecorder{ client: c, - node: n, eventRecorder: eventRecorder, eventBroadcaster: eventBroadcaster, } @@ -37,9 +36,9 @@ func NewEventRecorder(c snclientset.Interface, n string, kubeclient kubernetes.I // SendEvent Send an Event on the NodeState object func (e *EventRecorder) SendEvent(eventType string, msg string) { - nodeState, err := e.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).Get(context.Background(), e.node, metav1.GetOptions{}) + nodeState, err := e.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).Get(context.Background(), vars.NodeName, metav1.GetOptions{}) if err != nil { - log.Log.V(2).Error(err, "SendEvent(): Failed to fetch node state, skip SendEvent", "name", e.node) + log.Log.V(2).Error(err, "SendEvent(): Failed to fetch node state, skip SendEvent", "name", vars.NodeName) return } e.eventRecorder.Event(nodeState, corev1.EventTypeNormal, eventType, msg) diff --git a/pkg/daemon/plugin.go b/pkg/daemon/plugin.go index 09c69271c..8cb344347 100644 --- a/pkg/daemon/plugin.go +++ b/pkg/daemon/plugin.go @@ -6,17 +6,18 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" genericplugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/generic" intelplugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/intel" k8splugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/k8s" mellanoxplugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/mellanox" virtualplugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins/virtual" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) -var VendorPluginMap = map[string]func() (plugin.VendorPlugin, error){ +var VendorPluginMap = map[string]func(helpers helper.HostHelpersInterface) (plugin.VendorPlugin, error){ "8086": intelplugin.NewIntelPlugin, "15b3": mellanoxplugin.NewMellanoxPlugin, } @@ -29,33 +30,33 @@ var ( K8sPlugin = k8splugin.NewK8sPlugin ) -func enablePlugins(platform utils.PlatformType, useSystemdService bool, ns *sriovnetworkv1.SriovNetworkNodeState, hostManager host.HostManagerInterface, storeManager utils.StoreManagerInterface) (map[string]plugin.VendorPlugin, error) { +func enablePlugins(ns *sriovnetworkv1.SriovNetworkNodeState, helpers helper.HostHelpersInterface) (map[string]plugin.VendorPlugin, error) { log.Log.Info("enableVendorPlugins(): enabling plugins") enabledPlugins := map[string]plugin.VendorPlugin{} - if platform == utils.VirtualOpenStack { - virtualPlugin, err := VirtualPlugin(false) + if vars.PlatformType == consts.VirtualOpenStack { + virtualPlugin, err := VirtualPlugin(helpers) if err != nil { log.Log.Error(err, "enableVendorPlugins(): failed to load the virtual plugin") return nil, err } enabledPlugins[virtualPlugin.Name()] = virtualPlugin } else { - enabledVendorPlugins, err := registerVendorPlugins(ns) + enabledVendorPlugins, err := registerVendorPlugins(ns, helpers) if err != nil { return nil, err } enabledPlugins = enabledVendorPlugins - if utils.ClusterType != utils.ClusterTypeOpenshift { - k8sPlugin, err := K8sPlugin(useSystemdService) + if vars.ClusterType != consts.ClusterTypeOpenshift { + k8sPlugin, err := K8sPlugin(helpers) if err != nil { log.Log.Error(err, "enableVendorPlugins(): failed to load the k8s plugin") return nil, err } enabledPlugins[k8sPlugin.Name()] = k8sPlugin } - genericPlugin, err := GenericPlugin(false, hostManager, storeManager) + genericPlugin, err := GenericPlugin(helpers) if err != nil { log.Log.Error(err, "enableVendorPlugins(): failed to load the generic plugin") return nil, err @@ -71,17 +72,19 @@ func enablePlugins(platform utils.PlatformType, useSystemdService bool, ns *srio return enabledPlugins, nil } -func registerVendorPlugins(ns *sriovnetworkv1.SriovNetworkNodeState) (map[string]plugin.VendorPlugin, error) { +func registerVendorPlugins(ns *sriovnetworkv1.SriovNetworkNodeState, helpers helper.HostHelpersInterface) (map[string]plugin.VendorPlugin, error) { vendorPlugins := map[string]plugin.VendorPlugin{} for _, iface := range ns.Status.Interfaces { if val, ok := VendorPluginMap[iface.Vendor]; ok { - plug, err := val() + plug, err := val(helpers) if err != nil { log.Log.Error(err, "registerVendorPlugins(): failed to load plugin", "plugin-name", plug.Name()) return vendorPlugins, fmt.Errorf("registerVendorPlugins(): failed to load the %s plugin error: %v", plug.Name(), err) } - vendorPlugins[plug.Name()] = plug + if _, ok := vendorPlugins[plug.Name()]; !ok { + vendorPlugins[plug.Name()] = plug + } } } diff --git a/pkg/daemon/writer.go b/pkg/daemon/writer.go index a9e65a417..c796546ef 100644 --- a/pkg/daemon/writer.go +++ b/pkg/daemon/writer.go @@ -16,7 +16,10 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) const ( @@ -25,62 +28,51 @@ const ( ) type NodeStateStatusWriter struct { - client snclientset.Interface - node string - status sriovnetworkv1.SriovNetworkNodeStateStatus - OnHeartbeatFailure func() - openStackDevicesInfo utils.OSPDevicesInfo - withUnsupportedDevices bool - storeManager utils.StoreManagerInterface - eventRecorder *EventRecorder + client snclientset.Interface + status sriovnetworkv1.SriovNetworkNodeStateStatus + OnHeartbeatFailure func() + platformHelper platforms.Interface + hostHelper helper.HostHelpersInterface + eventRecorder *EventRecorder } // NewNodeStateStatusWriter Create a new NodeStateStatusWriter -func NewNodeStateStatusWriter(c snclientset.Interface, n string, f func(), er *EventRecorder, devMode bool) *NodeStateStatusWriter { +func NewNodeStateStatusWriter(c snclientset.Interface, + f func(), er *EventRecorder, + hostHelper helper.HostHelpersInterface, + platformHelper platforms.Interface) *NodeStateStatusWriter { return &NodeStateStatusWriter{ - client: c, - node: n, - OnHeartbeatFailure: f, - eventRecorder: er, - withUnsupportedDevices: devMode, + client: c, + OnHeartbeatFailure: f, + eventRecorder: er, + hostHelper: hostHelper, + platformHelper: platformHelper, } } // RunOnce initial the interface status for both baremetal and virtual environments -func (w *NodeStateStatusWriter) RunOnce(destDir string, platformType utils.PlatformType) error { +func (w *NodeStateStatusWriter) RunOnce() error { log.Log.V(0).Info("RunOnce()") msg := Message{} - storeManager, err := utils.NewStoreManager(false) - if err != nil { - log.Log.Error(err, "failed to create store manager") - return err - } - w.storeManager = storeManager - - if platformType == utils.VirtualOpenStack { - ns, err := w.getCheckPointNodeState(destDir) + if vars.PlatformType == consts.VirtualOpenStack { + ns, err := w.getCheckPointNodeState() if err != nil { return err } if ns == nil { - metaData, networkData, err := utils.GetOpenstackData(true) - if err != nil { - log.Log.Error(err, "RunOnce(): failed to read OpenStack data") - } - - w.openStackDevicesInfo, err = utils.CreateOpenstackDevicesInfo(metaData, networkData) + err = w.platformHelper.CreateOpenstackDevicesInfo() if err != nil { return err } } else { - w.openStackDevicesInfo = utils.CreateOpenstackDevicesInfoFromNodeStatus(ns) + w.platformHelper.CreateOpenstackDevicesInfoFromNodeStatus(ns) } } log.Log.V(0).Info("RunOnce(): first poll for nic status") - if err := w.pollNicStatus(platformType); err != nil { + if err := w.pollNicStatus(); err != nil { log.Log.Error(err, "RunOnce(): first poll failed") } @@ -88,12 +80,12 @@ func (w *NodeStateStatusWriter) RunOnce(destDir string, platformType utils.Platf if err != nil { log.Log.Error(err, "RunOnce(): first writing to node status failed") } - return w.writeCheckpointFile(ns, destDir) + return w.writeCheckpointFile(ns) } // Run reads from the writer channel and sets the interface status. It will // return if the stop channel is closed. Intended to be run via a goroutine. -func (w *NodeStateStatusWriter) Run(stop <-chan struct{}, refresh <-chan Message, syncCh chan<- struct{}, platformType utils.PlatformType) error { +func (w *NodeStateStatusWriter) Run(stop <-chan struct{}, refresh <-chan Message, syncCh chan<- struct{}) error { log.Log.V(0).Info("Run(): start writer") msg := Message{} @@ -104,7 +96,7 @@ func (w *NodeStateStatusWriter) Run(stop <-chan struct{}, refresh <-chan Message return nil case msg = <-refresh: log.Log.V(0).Info("Run(): refresh trigger") - if err := w.pollNicStatus(platformType); err != nil { + if err := w.pollNicStatus(); err != nil { continue } _, err := w.setNodeStateStatus(msg) @@ -114,7 +106,7 @@ func (w *NodeStateStatusWriter) Run(stop <-chan struct{}, refresh <-chan Message syncCh <- struct{}{} case <-time.After(30 * time.Second): log.Log.V(2).Info("Run(): period refresh") - if err := w.pollNicStatus(platformType); err != nil { + if err := w.pollNicStatus(); err != nil { continue } w.setNodeStateStatus(msg) @@ -122,15 +114,15 @@ func (w *NodeStateStatusWriter) Run(stop <-chan struct{}, refresh <-chan Message } } -func (w *NodeStateStatusWriter) pollNicStatus(platformType utils.PlatformType) error { +func (w *NodeStateStatusWriter) pollNicStatus() error { log.Log.V(2).Info("pollNicStatus()") var iface []sriovnetworkv1.InterfaceExt var err error - if platformType == utils.VirtualOpenStack { - iface, err = utils.DiscoverSriovDevicesVirtual(w.openStackDevicesInfo) + if vars.PlatformType == consts.VirtualOpenStack { + iface, err = w.platformHelper.DiscoverSriovDevicesVirtual() } else { - iface, err = utils.DiscoverSriovDevices(w.withUnsupportedDevices, w.storeManager) + iface, err = w.hostHelper.DiscoverSriovDevices(w.hostHelper) } if err != nil { return err @@ -158,7 +150,7 @@ func (w *NodeStateStatusWriter) updateNodeStateStatusRetry(f func(*sriovnetworkv lastError = n.Status.LastSyncError var err error - nodeState, err = w.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).UpdateStatus(context.Background(), n, metav1.UpdateOptions{}) + nodeState, err = w.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).UpdateStatus(context.Background(), n, metav1.UpdateOptions{}) if err != nil { log.Log.V(0).Error(err, "updateNodeStateStatusRetry(): fail to update the node status") } @@ -177,7 +169,7 @@ func (w *NodeStateStatusWriter) updateNodeStateStatusRetry(f func(*sriovnetworkv func (w *NodeStateStatusWriter) setNodeStateStatus(msg Message) (*sriovnetworkv1.SriovNetworkNodeState, error) { nodeState, err := w.updateNodeStateStatusRetry(func(nodeState *sriovnetworkv1.SriovNetworkNodeState) { nodeState.Status.Interfaces = w.status.Interfaces - if msg.lastSyncError != "" || msg.syncStatus == syncStatusSucceeded { + if msg.lastSyncError != "" || msg.syncStatus == consts.SyncStatusSucceeded { // clear lastSyncError when sync Succeeded nodeState.Status.LastSyncError = msg.lastSyncError } @@ -215,33 +207,33 @@ func (w *NodeStateStatusWriter) getNodeState() (*sriovnetworkv1.SriovNetworkNode var lastErr error var n *sriovnetworkv1.SriovNetworkNodeState err := wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) { - n, lastErr = w.client.SriovnetworkV1().SriovNetworkNodeStates(namespace).Get(context.Background(), w.node, metav1.GetOptions{}) + n, lastErr = w.client.SriovnetworkV1().SriovNetworkNodeStates(vars.Namespace).Get(context.Background(), vars.NodeName, metav1.GetOptions{}) if lastErr == nil { return true, nil } - log.Log.Error(lastErr, "getNodeState(): Failed to fetch node state, close all connections and retry...", "name", w.node) + log.Log.Error(lastErr, "getNodeState(): Failed to fetch node state, close all connections and retry...", "name", vars.NodeName) // Use the Get() also as an client-go keepalive indicator for the TCP connection. w.OnHeartbeatFailure() return false, nil }) if err != nil { if err == wait.ErrWaitTimeout { - return nil, errors.Wrapf(lastErr, "Timed out trying to fetch node %s", w.node) + return nil, errors.Wrapf(lastErr, "Timed out trying to fetch node %s", vars.NodeName) } return nil, err } return n, nil } -func (w *NodeStateStatusWriter) writeCheckpointFile(ns *sriovnetworkv1.SriovNetworkNodeState, destDir string) error { - configdir := filepath.Join(destDir, CheckpointFileName) +func (w *NodeStateStatusWriter) writeCheckpointFile(ns *sriovnetworkv1.SriovNetworkNodeState) error { + configdir := filepath.Join(vars.Destdir, CheckpointFileName) file, err := os.OpenFile(configdir, os.O_RDWR|os.O_CREATE, 0644) if err != nil { return err } defer file.Close() log.Log.Info("writeCheckpointFile(): try to decode the checkpoint file") - if err = json.NewDecoder(file).Decode(&utils.InitialState); err != nil { + if err = json.NewDecoder(file).Decode(&sriovnetworkv1.InitialState); err != nil { log.Log.V(2).Error(err, "writeCheckpointFile(): fail to decode, writing new file instead") log.Log.Info("writeCheckpointFile(): write checkpoint file") if err = file.Truncate(0); err != nil { @@ -253,14 +245,14 @@ func (w *NodeStateStatusWriter) writeCheckpointFile(ns *sriovnetworkv1.SriovNetw if err = json.NewEncoder(file).Encode(*ns); err != nil { return err } - utils.InitialState = *ns + sriovnetworkv1.InitialState = *ns } return nil } -func (w *NodeStateStatusWriter) getCheckPointNodeState(destDir string) (*sriovnetworkv1.SriovNetworkNodeState, error) { +func (w *NodeStateStatusWriter) getCheckPointNodeState() (*sriovnetworkv1.SriovNetworkNodeState, error) { log.Log.Info("getCheckPointNodeState()") - configdir := filepath.Join(destDir, CheckpointFileName) + configdir := filepath.Join(vars.Destdir, CheckpointFileName) file, err := os.OpenFile(configdir, os.O_RDONLY, 0644) if err != nil { if os.IsNotExist(err) { @@ -269,9 +261,9 @@ func (w *NodeStateStatusWriter) getCheckPointNodeState(destDir string) (*sriovne return nil, err } defer file.Close() - if err = json.NewDecoder(file).Decode(&utils.InitialState); err != nil { + if err = json.NewDecoder(file).Decode(&sriovnetworkv1.InitialState); err != nil { return nil, err } - return &utils.InitialState, nil + return &sriovnetworkv1.InitialState, nil } diff --git a/pkg/helper/host.go b/pkg/helper/host.go new file mode 100644 index 000000000..5829d161a --- /dev/null +++ b/pkg/helper/host.go @@ -0,0 +1,50 @@ +package helper + +import ( + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + mlx "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" +) + +//go:generate ../../bin/mockgen -destination mock/mock_helper.go -source host.go +type HostHelpersInterface interface { + utils.CmdInterface + host.HostManagerInterface + store.ManagerInterface + mlx.MellanoxInterface +} + +type hostHelpers struct { + utils.CmdInterface + host.HostManagerInterface + store.ManagerInterface + mlx.MellanoxInterface +} + +// Use for unit tests +func NewHostHelpers(utilsHelper utils.CmdInterface, + hostManager host.HostManagerInterface, + storeManager store.ManagerInterface, + mlxHelper mlx.MellanoxInterface) HostHelpersInterface { + return &hostHelpers{utilsHelper, hostManager, storeManager, mlxHelper} +} + +func NewDefaultHostHelpers() (HostHelpersInterface, error) { + utilsHelper := utils.New() + mlxHelper := mlx.New(utilsHelper) + hostManager := host.NewHostManager(utilsHelper) + storeManager, err := store.NewManager() + if err != nil { + log.Log.Error(err, "failed to create store manager") + return nil, err + } + + return &hostHelpers{ + utilsHelper, + hostManager, + storeManager, + mlxHelper}, nil +} diff --git a/pkg/helper/mock/mock_helper.go b/pkg/helper/mock/mock_helper.go new file mode 100644 index 000000000..a8e417e49 --- /dev/null +++ b/pkg/helper/mock/mock_helper.go @@ -0,0 +1,1180 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: host.go + +// Package mock_helper is a generated GoMock package. +package mock_helper + +import ( + reflect "reflect" + + unit "github.com/coreos/go-systemd/v22/unit" + gomock "github.com/golang/mock/gomock" + ghw "github.com/jaypipes/ghw" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + store "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store" + types "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + mlxutils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" + netlink "github.com/vishvananda/netlink" +) + +// MockHostHelpersInterface is a mock of HostHelpersInterface interface. +type MockHostHelpersInterface struct { + ctrl *gomock.Controller + recorder *MockHostHelpersInterfaceMockRecorder +} + +// MockHostHelpersInterfaceMockRecorder is the mock recorder for MockHostHelpersInterface. +type MockHostHelpersInterfaceMockRecorder struct { + mock *MockHostHelpersInterface +} + +// NewMockHostHelpersInterface creates a new mock instance. +func NewMockHostHelpersInterface(ctrl *gomock.Controller) *MockHostHelpersInterface { + mock := &MockHostHelpersInterface{ctrl: ctrl} + mock.recorder = &MockHostHelpersInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHostHelpersInterface) EXPECT() *MockHostHelpersInterfaceMockRecorder { + return m.recorder +} + +// AddUdevRule mocks base method. +func (m *MockHostHelpersInterface) AddUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddUdevRule indicates an expected call of AddUdevRule. +func (mr *MockHostHelpersInterfaceMockRecorder) AddUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUdevRule", reflect.TypeOf((*MockHostHelpersInterface)(nil).AddUdevRule), pfPciAddress) +} + +// AddVfRepresentorUdevRule mocks base method. +func (m *MockHostHelpersInterface) AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddVfRepresentorUdevRule", pfPciAddress, pfName, pfSwitchID, pfSwitchPort) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddVfRepresentorUdevRule indicates an expected call of AddVfRepresentorUdevRule. +func (mr *MockHostHelpersInterfaceMockRecorder) AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVfRepresentorUdevRule", reflect.TypeOf((*MockHostHelpersInterface)(nil).AddVfRepresentorUdevRule), pfPciAddress, pfName, pfSwitchID, pfSwitchPort) +} + +// BindDefaultDriver mocks base method. +func (m *MockHostHelpersInterface) BindDefaultDriver(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDefaultDriver", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDefaultDriver indicates an expected call of BindDefaultDriver. +func (mr *MockHostHelpersInterfaceMockRecorder) BindDefaultDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDefaultDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).BindDefaultDriver), pciAddr) +} + +// BindDpdkDriver mocks base method. +func (m *MockHostHelpersInterface) BindDpdkDriver(pciAddr, driver string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDpdkDriver", pciAddr, driver) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDpdkDriver indicates an expected call of BindDpdkDriver. +func (mr *MockHostHelpersInterfaceMockRecorder) BindDpdkDriver(pciAddr, driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDpdkDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).BindDpdkDriver), pciAddr, driver) +} + +// BindDriverByBusAndDevice mocks base method. +func (m *MockHostHelpersInterface) BindDriverByBusAndDevice(bus, device, driver string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDriverByBusAndDevice", bus, device, driver) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDriverByBusAndDevice indicates an expected call of BindDriverByBusAndDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) BindDriverByBusAndDevice(bus, device, driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDriverByBusAndDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).BindDriverByBusAndDevice), bus, device, driver) +} + +// Chroot mocks base method. +func (m *MockHostHelpersInterface) Chroot(arg0 string) (func() error, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Chroot", arg0) + ret0, _ := ret[0].(func() error) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Chroot indicates an expected call of Chroot. +func (mr *MockHostHelpersInterfaceMockRecorder) Chroot(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Chroot", reflect.TypeOf((*MockHostHelpersInterface)(nil).Chroot), arg0) +} + +// ClearPCIAddressFolder mocks base method. +func (m *MockHostHelpersInterface) ClearPCIAddressFolder() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClearPCIAddressFolder") + ret0, _ := ret[0].(error) + return ret0 +} + +// ClearPCIAddressFolder indicates an expected call of ClearPCIAddressFolder. +func (mr *MockHostHelpersInterfaceMockRecorder) ClearPCIAddressFolder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearPCIAddressFolder", reflect.TypeOf((*MockHostHelpersInterface)(nil).ClearPCIAddressFolder)) +} + +// CompareServices mocks base method. +func (m *MockHostHelpersInterface) CompareServices(serviceA, serviceB *types.Service) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CompareServices", serviceA, serviceB) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CompareServices indicates an expected call of CompareServices. +func (mr *MockHostHelpersInterfaceMockRecorder) CompareServices(serviceA, serviceB interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareServices", reflect.TypeOf((*MockHostHelpersInterface)(nil).CompareServices), serviceA, serviceB) +} + +// ConfigSriovDevice mocks base method. +func (m *MockHostHelpersInterface) ConfigSriovDevice(iface *v1.Interface, ifaceStatus *v1.InterfaceExt) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovDevice", iface, ifaceStatus) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovDevice indicates an expected call of ConfigSriovDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) ConfigSriovDevice(iface, ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).ConfigSriovDevice), iface, ifaceStatus) +} + +// ConfigSriovDeviceVirtual mocks base method. +func (m *MockHostHelpersInterface) ConfigSriovDeviceVirtual(iface *v1.Interface) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovDeviceVirtual", iface) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovDeviceVirtual indicates an expected call of ConfigSriovDeviceVirtual. +func (mr *MockHostHelpersInterfaceMockRecorder) ConfigSriovDeviceVirtual(iface interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovDeviceVirtual", reflect.TypeOf((*MockHostHelpersInterface)(nil).ConfigSriovDeviceVirtual), iface) +} + +// ConfigSriovInterfaces mocks base method. +func (m *MockHostHelpersInterface) ConfigSriovInterfaces(storeManager store.ManagerInterface, interfaces []v1.Interface, ifaceStatuses []v1.InterfaceExt, pfsToConfig map[string]bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovInterfaces", storeManager, interfaces, ifaceStatuses, pfsToConfig) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovInterfaces indicates an expected call of ConfigSriovInterfaces. +func (mr *MockHostHelpersInterfaceMockRecorder) ConfigSriovInterfaces(storeManager, interfaces, ifaceStatuses, pfsToConfig interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovInterfaces", reflect.TypeOf((*MockHostHelpersInterface)(nil).ConfigSriovInterfaces), storeManager, interfaces, ifaceStatuses, pfsToConfig) +} + +// CreateVDPADevice mocks base method. +func (m *MockHostHelpersInterface) CreateVDPADevice(pciAddr, vdpaType string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateVDPADevice", pciAddr, vdpaType) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateVDPADevice indicates an expected call of CreateVDPADevice. +func (mr *MockHostHelpersInterfaceMockRecorder) CreateVDPADevice(pciAddr, vdpaType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVDPADevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).CreateVDPADevice), pciAddr, vdpaType) +} + +// DeleteVDPADevice mocks base method. +func (m *MockHostHelpersInterface) DeleteVDPADevice(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteVDPADevice", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteVDPADevice indicates an expected call of DeleteVDPADevice. +func (mr *MockHostHelpersInterfaceMockRecorder) DeleteVDPADevice(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVDPADevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).DeleteVDPADevice), pciAddr) +} + +// DiscoverSriovDevices mocks base method. +func (m *MockHostHelpersInterface) DiscoverSriovDevices(storeManager store.ManagerInterface) ([]v1.InterfaceExt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverSriovDevices", storeManager) + ret0, _ := ret[0].([]v1.InterfaceExt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DiscoverSriovDevices indicates an expected call of DiscoverSriovDevices. +func (mr *MockHostHelpersInterfaceMockRecorder) DiscoverSriovDevices(storeManager interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverSriovDevices", reflect.TypeOf((*MockHostHelpersInterface)(nil).DiscoverSriovDevices), storeManager) +} + +// DiscoverVDPAType mocks base method. +func (m *MockHostHelpersInterface) DiscoverVDPAType(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverVDPAType", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// DiscoverVDPAType indicates an expected call of DiscoverVDPAType. +func (mr *MockHostHelpersInterfaceMockRecorder) DiscoverVDPAType(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverVDPAType", reflect.TypeOf((*MockHostHelpersInterface)(nil).DiscoverVDPAType), pciAddr) +} + +// EnableRDMA mocks base method. +func (m *MockHostHelpersInterface) EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnableRDMA", conditionFilePath, serviceName, packageManager) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnableRDMA indicates an expected call of EnableRDMA. +func (mr *MockHostHelpersInterfaceMockRecorder) EnableRDMA(conditionFilePath, serviceName, packageManager interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRDMA", reflect.TypeOf((*MockHostHelpersInterface)(nil).EnableRDMA), conditionFilePath, serviceName, packageManager) +} + +// EnableRDMAOnRHELMachine mocks base method. +func (m *MockHostHelpersInterface) EnableRDMAOnRHELMachine() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnableRDMAOnRHELMachine") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnableRDMAOnRHELMachine indicates an expected call of EnableRDMAOnRHELMachine. +func (mr *MockHostHelpersInterfaceMockRecorder) EnableRDMAOnRHELMachine() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRDMAOnRHELMachine", reflect.TypeOf((*MockHostHelpersInterface)(nil).EnableRDMAOnRHELMachine)) +} + +// EnableService mocks base method. +func (m *MockHostHelpersInterface) EnableService(service *types.Service) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnableService", service) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnableService indicates an expected call of EnableService. +func (mr *MockHostHelpersInterfaceMockRecorder) EnableService(service interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableService", reflect.TypeOf((*MockHostHelpersInterface)(nil).EnableService), service) +} + +// GetCheckPointNodeState mocks base method. +func (m *MockHostHelpersInterface) GetCheckPointNodeState() (*v1.SriovNetworkNodeState, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCheckPointNodeState") + ret0, _ := ret[0].(*v1.SriovNetworkNodeState) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCheckPointNodeState indicates an expected call of GetCheckPointNodeState. +func (mr *MockHostHelpersInterfaceMockRecorder) GetCheckPointNodeState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCheckPointNodeState", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetCheckPointNodeState)) +} + +// GetCurrentKernelArgs mocks base method. +func (m *MockHostHelpersInterface) GetCurrentKernelArgs() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentKernelArgs") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentKernelArgs indicates an expected call of GetCurrentKernelArgs. +func (mr *MockHostHelpersInterfaceMockRecorder) GetCurrentKernelArgs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentKernelArgs", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetCurrentKernelArgs)) +} + +// GetLinkType mocks base method. +func (m *MockHostHelpersInterface) GetLinkType(ifaceStatus v1.InterfaceExt) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLinkType", ifaceStatus) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetLinkType indicates an expected call of GetLinkType. +func (mr *MockHostHelpersInterfaceMockRecorder) GetLinkType(ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLinkType", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetLinkType), ifaceStatus) +} + +// GetMellanoxBlueFieldMode mocks base method. +func (m *MockHostHelpersInterface) GetMellanoxBlueFieldMode(arg0 string) (mlxutils.BlueFieldMode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMellanoxBlueFieldMode", arg0) + ret0, _ := ret[0].(mlxutils.BlueFieldMode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMellanoxBlueFieldMode indicates an expected call of GetMellanoxBlueFieldMode. +func (mr *MockHostHelpersInterfaceMockRecorder) GetMellanoxBlueFieldMode(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMellanoxBlueFieldMode", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetMellanoxBlueFieldMode), arg0) +} + +// GetMlxNicFwData mocks base method. +func (m *MockHostHelpersInterface) GetMlxNicFwData(pciAddress string) (*mlxutils.MlxNic, *mlxutils.MlxNic, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMlxNicFwData", pciAddress) + ret0, _ := ret[0].(*mlxutils.MlxNic) + ret1, _ := ret[1].(*mlxutils.MlxNic) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetMlxNicFwData indicates an expected call of GetMlxNicFwData. +func (mr *MockHostHelpersInterfaceMockRecorder) GetMlxNicFwData(pciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMlxNicFwData", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetMlxNicFwData), pciAddress) +} + +// GetNetDevLinkSpeed mocks base method. +func (m *MockHostHelpersInterface) GetNetDevLinkSpeed(name string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetDevLinkSpeed", name) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetDevLinkSpeed indicates an expected call of GetNetDevLinkSpeed. +func (mr *MockHostHelpersInterfaceMockRecorder) GetNetDevLinkSpeed(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetDevLinkSpeed", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetNetDevLinkSpeed), name) +} + +// GetNetDevMac mocks base method. +func (m *MockHostHelpersInterface) GetNetDevMac(name string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetDevMac", name) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetDevMac indicates an expected call of GetNetDevMac. +func (mr *MockHostHelpersInterfaceMockRecorder) GetNetDevMac(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetDevMac", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetNetDevMac), name) +} + +// GetNetdevMTU mocks base method. +func (m *MockHostHelpersInterface) GetNetdevMTU(pciAddr string) int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetdevMTU", pciAddr) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetNetdevMTU indicates an expected call of GetNetdevMTU. +func (mr *MockHostHelpersInterfaceMockRecorder) GetNetdevMTU(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetdevMTU", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetNetdevMTU), pciAddr) +} + +// GetNicSriovMode mocks base method. +func (m *MockHostHelpersInterface) GetNicSriovMode(pciAddr string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNicSriovMode", pciAddr) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNicSriovMode indicates an expected call of GetNicSriovMode. +func (mr *MockHostHelpersInterfaceMockRecorder) GetNicSriovMode(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNicSriovMode", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetNicSriovMode), pciAddr) +} + +// GetOSPrettyName mocks base method. +func (m *MockHostHelpersInterface) GetOSPrettyName() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOSPrettyName") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOSPrettyName indicates an expected call of GetOSPrettyName. +func (mr *MockHostHelpersInterfaceMockRecorder) GetOSPrettyName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOSPrettyName", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetOSPrettyName)) +} + +// GetPhysPortName mocks base method. +func (m *MockHostHelpersInterface) GetPhysPortName(name string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhysPortName", name) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPhysPortName indicates an expected call of GetPhysPortName. +func (mr *MockHostHelpersInterfaceMockRecorder) GetPhysPortName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhysPortName", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetPhysPortName), name) +} + +// GetPhysSwitchID mocks base method. +func (m *MockHostHelpersInterface) GetPhysSwitchID(name string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhysSwitchID", name) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPhysSwitchID indicates an expected call of GetPhysSwitchID. +func (mr *MockHostHelpersInterfaceMockRecorder) GetPhysSwitchID(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhysSwitchID", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetPhysSwitchID), name) +} + +// GetVfInfo mocks base method. +func (m *MockHostHelpersInterface) GetVfInfo(pciAddr string, devices []*ghw.PCIDevice) v1.VirtualFunction { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVfInfo", pciAddr, devices) + ret0, _ := ret[0].(v1.VirtualFunction) + return ret0 +} + +// GetVfInfo indicates an expected call of GetVfInfo. +func (mr *MockHostHelpersInterfaceMockRecorder) GetVfInfo(pciAddr, devices interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVfInfo", reflect.TypeOf((*MockHostHelpersInterface)(nil).GetVfInfo), pciAddr, devices) +} + +// HasDriver mocks base method. +func (m *MockHostHelpersInterface) HasDriver(pciAddr string) (bool, string) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasDriver", pciAddr) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(string) + return ret0, ret1 +} + +// HasDriver indicates an expected call of HasDriver. +func (mr *MockHostHelpersInterfaceMockRecorder) HasDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).HasDriver), pciAddr) +} + +// InstallRDMA mocks base method. +func (m *MockHostHelpersInterface) InstallRDMA(packageManager string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstallRDMA", packageManager) + ret0, _ := ret[0].(error) + return ret0 +} + +// InstallRDMA indicates an expected call of InstallRDMA. +func (mr *MockHostHelpersInterfaceMockRecorder) InstallRDMA(packageManager interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstallRDMA", reflect.TypeOf((*MockHostHelpersInterface)(nil).InstallRDMA), packageManager) +} + +// IsCoreOS mocks base method. +func (m *MockHostHelpersInterface) IsCoreOS() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsCoreOS") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsCoreOS indicates an expected call of IsCoreOS. +func (mr *MockHostHelpersInterfaceMockRecorder) IsCoreOS() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCoreOS", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsCoreOS)) +} + +// IsKernelArgsSet mocks base method. +func (m *MockHostHelpersInterface) IsKernelArgsSet(cmdLine, karg string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsKernelArgsSet", cmdLine, karg) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsKernelArgsSet indicates an expected call of IsKernelArgsSet. +func (mr *MockHostHelpersInterfaceMockRecorder) IsKernelArgsSet(cmdLine, karg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelArgsSet", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsKernelArgsSet), cmdLine, karg) +} + +// IsKernelLockdownMode mocks base method. +func (m *MockHostHelpersInterface) IsKernelLockdownMode() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsKernelLockdownMode") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsKernelLockdownMode indicates an expected call of IsKernelLockdownMode. +func (mr *MockHostHelpersInterfaceMockRecorder) IsKernelLockdownMode() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelLockdownMode", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsKernelLockdownMode)) +} + +// IsKernelModuleLoaded mocks base method. +func (m *MockHostHelpersInterface) IsKernelModuleLoaded(name string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsKernelModuleLoaded", name) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsKernelModuleLoaded indicates an expected call of IsKernelModuleLoaded. +func (mr *MockHostHelpersInterfaceMockRecorder) IsKernelModuleLoaded(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelModuleLoaded", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsKernelModuleLoaded), name) +} + +// IsRHELSystem mocks base method. +func (m *MockHostHelpersInterface) IsRHELSystem() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsRHELSystem") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsRHELSystem indicates an expected call of IsRHELSystem. +func (mr *MockHostHelpersInterfaceMockRecorder) IsRHELSystem() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRHELSystem", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsRHELSystem)) +} + +// IsServiceEnabled mocks base method. +func (m *MockHostHelpersInterface) IsServiceEnabled(servicePath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsServiceEnabled", servicePath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsServiceEnabled indicates an expected call of IsServiceEnabled. +func (mr *MockHostHelpersInterfaceMockRecorder) IsServiceEnabled(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsServiceEnabled", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsServiceEnabled), servicePath) +} + +// IsServiceExist mocks base method. +func (m *MockHostHelpersInterface) IsServiceExist(servicePath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsServiceExist", servicePath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsServiceExist indicates an expected call of IsServiceExist. +func (mr *MockHostHelpersInterfaceMockRecorder) IsServiceExist(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsServiceExist", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsServiceExist), servicePath) +} + +// IsSwitchdev mocks base method. +func (m *MockHostHelpersInterface) IsSwitchdev(name string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsSwitchdev", name) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsSwitchdev indicates an expected call of IsSwitchdev. +func (mr *MockHostHelpersInterfaceMockRecorder) IsSwitchdev(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSwitchdev", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsSwitchdev), name) +} + +// IsUbuntuSystem mocks base method. +func (m *MockHostHelpersInterface) IsUbuntuSystem() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsUbuntuSystem") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsUbuntuSystem indicates an expected call of IsUbuntuSystem. +func (mr *MockHostHelpersInterfaceMockRecorder) IsUbuntuSystem() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUbuntuSystem", reflect.TypeOf((*MockHostHelpersInterface)(nil).IsUbuntuSystem)) +} + +// LoadKernelModule mocks base method. +func (m *MockHostHelpersInterface) LoadKernelModule(name string, args ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{name} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "LoadKernelModule", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// LoadKernelModule indicates an expected call of LoadKernelModule. +func (mr *MockHostHelpersInterfaceMockRecorder) LoadKernelModule(name interface{}, args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{name}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadKernelModule", reflect.TypeOf((*MockHostHelpersInterface)(nil).LoadKernelModule), varargs...) +} + +// LoadPfsStatus mocks base method. +func (m *MockHostHelpersInterface) LoadPfsStatus(pciAddress string) (*v1.Interface, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadPfsStatus", pciAddress) + ret0, _ := ret[0].(*v1.Interface) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// LoadPfsStatus indicates an expected call of LoadPfsStatus. +func (mr *MockHostHelpersInterfaceMockRecorder) LoadPfsStatus(pciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPfsStatus", reflect.TypeOf((*MockHostHelpersInterface)(nil).LoadPfsStatus), pciAddress) +} + +// MlxConfigFW mocks base method. +func (m *MockHostHelpersInterface) MlxConfigFW(attributesToChange map[string]mlxutils.MlxNic) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MlxConfigFW", attributesToChange) + ret0, _ := ret[0].(error) + return ret0 +} + +// MlxConfigFW indicates an expected call of MlxConfigFW. +func (mr *MockHostHelpersInterfaceMockRecorder) MlxConfigFW(attributesToChange interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MlxConfigFW", reflect.TypeOf((*MockHostHelpersInterface)(nil).MlxConfigFW), attributesToChange) +} + +// MstConfigReadData mocks base method. +func (m *MockHostHelpersInterface) MstConfigReadData(arg0 string) (string, string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MstConfigReadData", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// MstConfigReadData indicates an expected call of MstConfigReadData. +func (mr *MockHostHelpersInterfaceMockRecorder) MstConfigReadData(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MstConfigReadData", reflect.TypeOf((*MockHostHelpersInterface)(nil).MstConfigReadData), arg0) +} + +// PrepareNMUdevRule mocks base method. +func (m *MockHostHelpersInterface) PrepareNMUdevRule(supportedVfIds []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrepareNMUdevRule", supportedVfIds) + ret0, _ := ret[0].(error) + return ret0 +} + +// PrepareNMUdevRule indicates an expected call of PrepareNMUdevRule. +func (mr *MockHostHelpersInterfaceMockRecorder) PrepareNMUdevRule(supportedVfIds interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareNMUdevRule", reflect.TypeOf((*MockHostHelpersInterface)(nil).PrepareNMUdevRule), supportedVfIds) +} + +// RdmaIsLoaded mocks base method. +func (m *MockHostHelpersInterface) RdmaIsLoaded() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RdmaIsLoaded") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RdmaIsLoaded indicates an expected call of RdmaIsLoaded. +func (mr *MockHostHelpersInterfaceMockRecorder) RdmaIsLoaded() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RdmaIsLoaded", reflect.TypeOf((*MockHostHelpersInterface)(nil).RdmaIsLoaded)) +} + +// ReadScriptManifestFile mocks base method. +func (m *MockHostHelpersInterface) ReadScriptManifestFile(path string) (*types.ScriptManifestFile, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadScriptManifestFile", path) + ret0, _ := ret[0].(*types.ScriptManifestFile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadScriptManifestFile indicates an expected call of ReadScriptManifestFile. +func (mr *MockHostHelpersInterfaceMockRecorder) ReadScriptManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadScriptManifestFile", reflect.TypeOf((*MockHostHelpersInterface)(nil).ReadScriptManifestFile), path) +} + +// ReadService mocks base method. +func (m *MockHostHelpersInterface) ReadService(servicePath string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadService", servicePath) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadService indicates an expected call of ReadService. +func (mr *MockHostHelpersInterfaceMockRecorder) ReadService(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadService", reflect.TypeOf((*MockHostHelpersInterface)(nil).ReadService), servicePath) +} + +// ReadServiceInjectionManifestFile mocks base method. +func (m *MockHostHelpersInterface) ReadServiceInjectionManifestFile(path string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadServiceInjectionManifestFile", path) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadServiceInjectionManifestFile indicates an expected call of ReadServiceInjectionManifestFile. +func (mr *MockHostHelpersInterfaceMockRecorder) ReadServiceInjectionManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadServiceInjectionManifestFile", reflect.TypeOf((*MockHostHelpersInterface)(nil).ReadServiceInjectionManifestFile), path) +} + +// ReadServiceManifestFile mocks base method. +func (m *MockHostHelpersInterface) ReadServiceManifestFile(path string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadServiceManifestFile", path) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadServiceManifestFile indicates an expected call of ReadServiceManifestFile. +func (mr *MockHostHelpersInterfaceMockRecorder) ReadServiceManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadServiceManifestFile", reflect.TypeOf((*MockHostHelpersInterface)(nil).ReadServiceManifestFile), path) +} + +// RebindVfToDefaultDriver mocks base method. +func (m *MockHostHelpersInterface) RebindVfToDefaultDriver(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RebindVfToDefaultDriver", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// RebindVfToDefaultDriver indicates an expected call of RebindVfToDefaultDriver. +func (mr *MockHostHelpersInterfaceMockRecorder) RebindVfToDefaultDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RebindVfToDefaultDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).RebindVfToDefaultDriver), pciAddr) +} + +// ReloadDriver mocks base method. +func (m *MockHostHelpersInterface) ReloadDriver(driver string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReloadDriver", driver) + ret0, _ := ret[0].(error) + return ret0 +} + +// ReloadDriver indicates an expected call of ReloadDriver. +func (mr *MockHostHelpersInterfaceMockRecorder) ReloadDriver(driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).ReloadDriver), driver) +} + +// RemoveFromService mocks base method. +func (m *MockHostHelpersInterface) RemoveFromService(service *types.Service, options ...*unit.UnitOption) (*types.Service, error) { + m.ctrl.T.Helper() + varargs := []interface{}{service} + for _, a := range options { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RemoveFromService", varargs...) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RemoveFromService indicates an expected call of RemoveFromService. +func (mr *MockHostHelpersInterfaceMockRecorder) RemoveFromService(service interface{}, options ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{service}, options...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFromService", reflect.TypeOf((*MockHostHelpersInterface)(nil).RemoveFromService), varargs...) +} + +// RemoveUdevRule mocks base method. +func (m *MockHostHelpersInterface) RemoveUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveUdevRule indicates an expected call of RemoveUdevRule. +func (mr *MockHostHelpersInterfaceMockRecorder) RemoveUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveUdevRule", reflect.TypeOf((*MockHostHelpersInterface)(nil).RemoveUdevRule), pfPciAddress) +} + +// RemoveVfRepresentorUdevRule mocks base method. +func (m *MockHostHelpersInterface) RemoveVfRepresentorUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveVfRepresentorUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveVfRepresentorUdevRule indicates an expected call of RemoveVfRepresentorUdevRule. +func (mr *MockHostHelpersInterfaceMockRecorder) RemoveVfRepresentorUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVfRepresentorUdevRule", reflect.TypeOf((*MockHostHelpersInterface)(nil).RemoveVfRepresentorUdevRule), pfPciAddress) +} + +// ResetSriovDevice mocks base method. +func (m *MockHostHelpersInterface) ResetSriovDevice(ifaceStatus v1.InterfaceExt) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetSriovDevice", ifaceStatus) + ret0, _ := ret[0].(error) + return ret0 +} + +// ResetSriovDevice indicates an expected call of ResetSriovDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) ResetSriovDevice(ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetSriovDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).ResetSriovDevice), ifaceStatus) +} + +// RunCommand mocks base method. +func (m *MockHostHelpersInterface) RunCommand(arg0 string, arg1 ...string) (string, string, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RunCommand", varargs...) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// RunCommand indicates an expected call of RunCommand. +func (mr *MockHostHelpersInterfaceMockRecorder) RunCommand(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunCommand", reflect.TypeOf((*MockHostHelpersInterface)(nil).RunCommand), varargs...) +} + +// SaveLastPfAppliedStatus mocks base method. +func (m *MockHostHelpersInterface) SaveLastPfAppliedStatus(PfInfo *v1.Interface) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveLastPfAppliedStatus", PfInfo) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveLastPfAppliedStatus indicates an expected call of SaveLastPfAppliedStatus. +func (mr *MockHostHelpersInterfaceMockRecorder) SaveLastPfAppliedStatus(PfInfo interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveLastPfAppliedStatus", reflect.TypeOf((*MockHostHelpersInterface)(nil).SaveLastPfAppliedStatus), PfInfo) +} + +// SetNetdevMTU mocks base method. +func (m *MockHostHelpersInterface) SetNetdevMTU(pciAddr string, mtu int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetNetdevMTU", pciAddr, mtu) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetNetdevMTU indicates an expected call of SetNetdevMTU. +func (mr *MockHostHelpersInterfaceMockRecorder) SetNetdevMTU(pciAddr, mtu interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetdevMTU", reflect.TypeOf((*MockHostHelpersInterface)(nil).SetNetdevMTU), pciAddr, mtu) +} + +// SetNicSriovMode mocks base method. +func (m *MockHostHelpersInterface) SetNicSriovMode(pciAddr, mode string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetNicSriovMode", pciAddr, mode) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetNicSriovMode indicates an expected call of SetNicSriovMode. +func (mr *MockHostHelpersInterfaceMockRecorder) SetNicSriovMode(pciAddr, mode interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNicSriovMode", reflect.TypeOf((*MockHostHelpersInterface)(nil).SetNicSriovMode), pciAddr, mode) +} + +// SetSriovNumVfs mocks base method. +func (m *MockHostHelpersInterface) SetSriovNumVfs(pciAddr string, numVfs int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetSriovNumVfs", pciAddr, numVfs) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetSriovNumVfs indicates an expected call of SetSriovNumVfs. +func (mr *MockHostHelpersInterfaceMockRecorder) SetSriovNumVfs(pciAddr, numVfs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSriovNumVfs", reflect.TypeOf((*MockHostHelpersInterface)(nil).SetSriovNumVfs), pciAddr, numVfs) +} + +// SetVfAdminMac mocks base method. +func (m *MockHostHelpersInterface) SetVfAdminMac(vfAddr string, pfLink, vfLink netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetVfAdminMac", vfAddr, pfLink, vfLink) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetVfAdminMac indicates an expected call of SetVfAdminMac. +func (mr *MockHostHelpersInterfaceMockRecorder) SetVfAdminMac(vfAddr, pfLink, vfLink interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVfAdminMac", reflect.TypeOf((*MockHostHelpersInterface)(nil).SetVfAdminMac), vfAddr, pfLink, vfLink) +} + +// SetVfGUID mocks base method. +func (m *MockHostHelpersInterface) SetVfGUID(vfAddr string, pfLink netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetVfGUID", vfAddr, pfLink) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetVfGUID indicates an expected call of SetVfGUID. +func (mr *MockHostHelpersInterfaceMockRecorder) SetVfGUID(vfAddr, pfLink interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVfGUID", reflect.TypeOf((*MockHostHelpersInterface)(nil).SetVfGUID), vfAddr, pfLink) +} + +// TriggerUdevEvent mocks base method. +func (m *MockHostHelpersInterface) TriggerUdevEvent() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TriggerUdevEvent") + ret0, _ := ret[0].(error) + return ret0 +} + +// TriggerUdevEvent indicates an expected call of TriggerUdevEvent. +func (mr *MockHostHelpersInterfaceMockRecorder) TriggerUdevEvent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TriggerUdevEvent", reflect.TypeOf((*MockHostHelpersInterface)(nil).TriggerUdevEvent)) +} + +// TryEnableRdma mocks base method. +func (m *MockHostHelpersInterface) TryEnableRdma() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryEnableRdma") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TryEnableRdma indicates an expected call of TryEnableRdma. +func (mr *MockHostHelpersInterfaceMockRecorder) TryEnableRdma() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryEnableRdma", reflect.TypeOf((*MockHostHelpersInterface)(nil).TryEnableRdma)) +} + +// TryEnableTun mocks base method. +func (m *MockHostHelpersInterface) TryEnableTun() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TryEnableTun") +} + +// TryEnableTun indicates an expected call of TryEnableTun. +func (mr *MockHostHelpersInterfaceMockRecorder) TryEnableTun() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryEnableTun", reflect.TypeOf((*MockHostHelpersInterface)(nil).TryEnableTun)) +} + +// TryEnableVhostNet mocks base method. +func (m *MockHostHelpersInterface) TryEnableVhostNet() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TryEnableVhostNet") +} + +// TryEnableVhostNet indicates an expected call of TryEnableVhostNet. +func (mr *MockHostHelpersInterfaceMockRecorder) TryEnableVhostNet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryEnableVhostNet", reflect.TypeOf((*MockHostHelpersInterface)(nil).TryEnableVhostNet)) +} + +// TryGetInterfaceName mocks base method. +func (m *MockHostHelpersInterface) TryGetInterfaceName(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryGetInterfaceName", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// TryGetInterfaceName indicates an expected call of TryGetInterfaceName. +func (mr *MockHostHelpersInterfaceMockRecorder) TryGetInterfaceName(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryGetInterfaceName", reflect.TypeOf((*MockHostHelpersInterface)(nil).TryGetInterfaceName), pciAddr) +} + +// TryToGetVirtualInterfaceName mocks base method. +func (m *MockHostHelpersInterface) TryToGetVirtualInterfaceName(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryToGetVirtualInterfaceName", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// TryToGetVirtualInterfaceName indicates an expected call of TryToGetVirtualInterfaceName. +func (mr *MockHostHelpersInterfaceMockRecorder) TryToGetVirtualInterfaceName(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryToGetVirtualInterfaceName", reflect.TypeOf((*MockHostHelpersInterface)(nil).TryToGetVirtualInterfaceName), pciAddr) +} + +// Unbind mocks base method. +func (m *MockHostHelpersInterface) Unbind(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Unbind", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// Unbind indicates an expected call of Unbind. +func (mr *MockHostHelpersInterfaceMockRecorder) Unbind(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unbind", reflect.TypeOf((*MockHostHelpersInterface)(nil).Unbind), pciAddr) +} + +// UnbindDriverByBusAndDevice mocks base method. +func (m *MockHostHelpersInterface) UnbindDriverByBusAndDevice(bus, device string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindDriverByBusAndDevice", bus, device) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindDriverByBusAndDevice indicates an expected call of UnbindDriverByBusAndDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) UnbindDriverByBusAndDevice(bus, device interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindDriverByBusAndDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).UnbindDriverByBusAndDevice), bus, device) +} + +// UnbindDriverIfNeeded mocks base method. +func (m *MockHostHelpersInterface) UnbindDriverIfNeeded(pciAddr string, isRdma bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindDriverIfNeeded", pciAddr, isRdma) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindDriverIfNeeded indicates an expected call of UnbindDriverIfNeeded. +func (mr *MockHostHelpersInterfaceMockRecorder) UnbindDriverIfNeeded(pciAddr, isRdma interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindDriverIfNeeded", reflect.TypeOf((*MockHostHelpersInterface)(nil).UnbindDriverIfNeeded), pciAddr, isRdma) +} + +// UpdateSystemService mocks base method. +func (m *MockHostHelpersInterface) UpdateSystemService(serviceObj *types.Service) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateSystemService", serviceObj) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateSystemService indicates an expected call of UpdateSystemService. +func (mr *MockHostHelpersInterfaceMockRecorder) UpdateSystemService(serviceObj interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSystemService", reflect.TypeOf((*MockHostHelpersInterface)(nil).UpdateSystemService), serviceObj) +} + +// VFIsReady mocks base method. +func (m *MockHostHelpersInterface) VFIsReady(pciAddr string) (netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VFIsReady", pciAddr) + ret0, _ := ret[0].(netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// VFIsReady indicates an expected call of VFIsReady. +func (mr *MockHostHelpersInterfaceMockRecorder) VFIsReady(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VFIsReady", reflect.TypeOf((*MockHostHelpersInterface)(nil).VFIsReady), pciAddr) +} + +// WriteCheckpointFile mocks base method. +func (m *MockHostHelpersInterface) WriteCheckpointFile(arg0 *v1.SriovNetworkNodeState) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteCheckpointFile", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteCheckpointFile indicates an expected call of WriteCheckpointFile. +func (mr *MockHostHelpersInterfaceMockRecorder) WriteCheckpointFile(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteCheckpointFile", reflect.TypeOf((*MockHostHelpersInterface)(nil).WriteCheckpointFile), arg0) +} + +// WriteSwitchdevConfFile mocks base method. +func (m *MockHostHelpersInterface) WriteSwitchdevConfFile(newState *v1.SriovNetworkNodeState, pfsToSkip map[string]bool) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteSwitchdevConfFile", newState, pfsToSkip) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WriteSwitchdevConfFile indicates an expected call of WriteSwitchdevConfFile. +func (mr *MockHostHelpersInterfaceMockRecorder) WriteSwitchdevConfFile(newState, pfsToSkip interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSwitchdevConfFile", reflect.TypeOf((*MockHostHelpersInterface)(nil).WriteSwitchdevConfFile), newState, pfsToSkip) +} diff --git a/pkg/host/host.go b/pkg/host/host.go deleted file mode 100644 index 241e6ba17..000000000 --- a/pkg/host/host.go +++ /dev/null @@ -1,457 +0,0 @@ -/* -Copyright 2023. - -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 host - -import ( - "fmt" - "os" - pathlib "path" - "strings" - - "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" -) - -const ( - hostPathFromDaemon = "/host" - redhatReleaseFile = "/etc/redhat-release" - rhelRDMAConditionFile = "/usr/libexec/rdma-init-kernel" - rhelRDMAServiceName = "rdma" - rhelPackageManager = "yum" - - ubuntuRDMAConditionFile = "/usr/sbin/rdma-ndd" - ubuntuRDMAServiceName = "rdma-ndd" - ubuntuPackageManager = "apt-get" - - genericOSReleaseFile = "/etc/os-release" -) - -// Contains all the host manipulation functions -// -//go:generate ../../bin/mockgen -destination mock/mock_host.go -source host.go -type HostManagerInterface interface { - TryEnableTun() - TryEnableVhostNet() - TryEnableRdma() (bool, error) - - // private functions - // part of the interface for the mock generation - LoadKernelModule(name string, args ...string) error - IsKernelModuleLoaded(string) (bool, error) - IsRHELSystem() (bool, error) - IsUbuntuSystem() (bool, error) - IsCoreOS() (bool, error) - RdmaIsLoaded() (bool, error) - EnableRDMA(string, string, string) (bool, error) - InstallRDMA(string) error - TriggerUdevEvent() error - ReloadDriver(string) error - EnableRDMAOnRHELMachine() (bool, error) - GetOSPrettyName() (string, error) -} - -type HostManager struct { - RunOnHost bool - cmd utils.CommandInterface -} - -func NewHostManager(runOnHost bool) HostManagerInterface { - return &HostManager{ - RunOnHost: runOnHost, - cmd: &utils.Command{}, - } -} - -func (h *HostManager) LoadKernelModule(name string, args ...string) error { - log.Log.Info("LoadKernelModule(): try to load kernel module", "name", name, "args", args) - chrootDefinition := getChrootExtension(h.RunOnHost) - cmdArgs := strings.Join(args, " ") - - // check if the driver is already loaded in to the system - isLoaded, err := h.IsKernelModuleLoaded(name) - if err != nil { - log.Log.Error(err, "LoadKernelModule(): failed to check if kernel module is already loaded", "name", name) - } - if isLoaded { - log.Log.Info("LoadKernelModule(): kernel module already loaded", "name", name) - return nil - } - - _, _, err = h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("%s modprobe %s %s", chrootDefinition, name, cmdArgs)) - if err != nil { - log.Log.Error(err, "LoadKernelModule(): failed to load kernel module with arguments", "name", name, "args", args) - return err - } - return nil -} - -func (h *HostManager) IsKernelModuleLoaded(kernelModuleName string) (bool, error) { - log.Log.Info("IsKernelModuleLoaded(): check if kernel module is loaded", "name", kernelModuleName) - chrootDefinition := getChrootExtension(h.RunOnHost) - - stdout, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("%s lsmod | grep \"^%s\"", chrootDefinition, kernelModuleName)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", - "name", kernelModuleName, "stderr", stderr.String()) - return false, err - } - log.Log.V(2).Info("IsKernelModuleLoaded():", "stdout", stdout.String()) - if stderr.Len() != 0 { - log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", "name", kernelModuleName, "stderr", stderr.String()) - return false, fmt.Errorf(stderr.String()) - } - - if stdout.Len() != 0 { - log.Log.Info("IsKernelModuleLoaded(): kernel module already loaded", "name", kernelModuleName) - return true, nil - } - - return false, nil -} - -func (h *HostManager) TryEnableTun() { - if err := h.LoadKernelModule("tun"); err != nil { - log.Log.Error(err, "tryEnableTun(): TUN kernel module not loaded") - } -} - -func (h *HostManager) TryEnableVhostNet() { - if err := h.LoadKernelModule("vhost_net"); err != nil { - log.Log.Error(err, "tryEnableVhostNet(): VHOST_NET kernel module not loaded") - } -} - -func (h *HostManager) TryEnableRdma() (bool, error) { - log.Log.V(2).Info("tryEnableRdma()") - chrootDefinition := getChrootExtension(h.RunOnHost) - - // check if the driver is already loaded in to the system - _, stderr, mlx4Err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx4_en' <(%s lsmod)", chrootDefinition)) - if mlx4Err != nil && stderr.Len() != 0 { - log.Log.Error(mlx4Err, "tryEnableRdma(): failed to check for kernel module 'mlx4_en'", "stderr", stderr.String()) - return false, fmt.Errorf(stderr.String()) - } - - _, stderr, mlx5Err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx5_core' <(%s lsmod)", chrootDefinition)) - if mlx5Err != nil && stderr.Len() != 0 { - log.Log.Error(mlx5Err, "tryEnableRdma(): failed to check for kernel module 'mlx5_core'", "stderr", stderr.String()) - return false, fmt.Errorf(stderr.String()) - } - - if mlx4Err != nil && mlx5Err != nil { - log.Log.Error(nil, "tryEnableRdma(): no RDMA capable devices") - return false, nil - } - - isRhelSystem, err := h.IsRHELSystem() - if err != nil { - log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on RHEL") - return false, err - } - - // RHEL check - if isRhelSystem { - return h.EnableRDMAOnRHELMachine() - } - - isUbuntuSystem, err := h.IsUbuntuSystem() - if err != nil { - log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on Ubuntu") - return false, err - } - - if isUbuntuSystem { - return h.EnableRDMAOnUbuntuMachine() - } - - osName, err := h.GetOSPrettyName() - if err != nil { - log.Log.Error(err, "tryEnableRdma(): failed to check OS name") - return false, err - } - - log.Log.Error(nil, "tryEnableRdma(): Unsupported OS", "name", osName) - return false, fmt.Errorf("unable to load RDMA unsupported OS: %s", osName) -} - -func (h *HostManager) EnableRDMAOnRHELMachine() (bool, error) { - log.Log.Info("EnableRDMAOnRHELMachine()") - isCoreOsSystem, err := h.IsCoreOS() - if err != nil { - log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if the machine runs CoreOS") - return false, err - } - - // CoreOS check - if isCoreOsSystem { - isRDMALoaded, err := h.RdmaIsLoaded() - if err != nil { - log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") - return false, err - } - - return isRDMALoaded, nil - } - - // RHEL - log.Log.Info("EnableRDMAOnRHELMachine(): enabling RDMA on RHEL machine") - isRDMAEnable, err := h.EnableRDMA(rhelRDMAConditionFile, rhelRDMAServiceName, rhelPackageManager) - if err != nil { - log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to enable RDMA on RHEL machine") - return false, err - } - - // check if we need to install rdma-core package - if isRDMAEnable { - isRDMALoaded, err := h.RdmaIsLoaded() - if err != nil { - log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") - return false, err - } - - // if ib kernel module is not loaded trigger a loading - if isRDMALoaded { - err = h.TriggerUdevEvent() - if err != nil { - log.Log.Error(err, "EnableRDMAOnRHELMachine() failed to trigger udev event") - return false, err - } - } - } - - return true, nil -} - -func (h *HostManager) EnableRDMAOnUbuntuMachine() (bool, error) { - log.Log.Info("EnableRDMAOnUbuntuMachine(): enabling RDMA on RHEL machine") - isRDMAEnable, err := h.EnableRDMA(ubuntuRDMAConditionFile, ubuntuRDMAServiceName, ubuntuPackageManager) - if err != nil { - log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to enable RDMA on Ubuntu machine") - return false, err - } - - // check if we need to install rdma-core package - if isRDMAEnable { - isRDMALoaded, err := h.RdmaIsLoaded() - if err != nil { - log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to check if RDMA kernel modules are loaded") - return false, err - } - - // if ib kernel module is not loaded trigger a loading - if isRDMALoaded { - err = h.TriggerUdevEvent() - if err != nil { - log.Log.Error(err, "EnableRDMAOnUbuntuMachine() failed to trigger udev event") - return false, err - } - } - } - - return true, nil -} - -func (h *HostManager) IsRHELSystem() (bool, error) { - log.Log.Info("IsRHELSystem(): checking for RHEL machine") - path := redhatReleaseFile - if !h.RunOnHost { - path = pathlib.Join(hostPathFromDaemon, path) - } - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - log.Log.V(2).Info("IsRHELSystem() not a RHEL machine") - return false, nil - } - - log.Log.Error(err, "IsRHELSystem() failed to check for os release file", "path", path) - return false, err - } - - return true, nil -} - -func (h *HostManager) IsCoreOS() (bool, error) { - log.Log.Info("IsCoreOS(): checking for CoreOS machine") - path := redhatReleaseFile - if !h.RunOnHost { - path = pathlib.Join(hostPathFromDaemon, path) - } - - data, err := os.ReadFile(path) - if err != nil { - log.Log.Error(err, "IsCoreOS(): failed to read RHEL release file on path", "path", path) - return false, err - } - - if strings.Contains(string(data), "CoreOS") { - return true, nil - } - - return false, nil -} - -func (h *HostManager) IsUbuntuSystem() (bool, error) { - log.Log.Info("IsUbuntuSystem(): checking for Ubuntu machine") - path := genericOSReleaseFile - if !h.RunOnHost { - path = pathlib.Join(hostPathFromDaemon, path) - } - - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - log.Log.Error(nil, "IsUbuntuSystem() os-release on path doesn't exist", "path", path) - return false, err - } - - log.Log.Error(err, "IsUbuntuSystem() failed to check for os release file", "path", path) - return false, err - } - - stdout, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("grep -i --quiet 'ubuntu' %s", path)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "IsUbuntuSystem(): failed to check for ubuntu operating system name in os-releasae file", "stderr", stderr.String()) - return false, fmt.Errorf(stderr.String()) - } - - if stdout.Len() > 0 { - return true, nil - } - - return false, nil -} - -func (h *HostManager) RdmaIsLoaded() (bool, error) { - log.Log.V(2).Info("RdmaIsLoaded()") - chrootDefinition := getChrootExtension(h.RunOnHost) - - // check if the driver is already loaded in to the system - _, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("grep --quiet '\\(^ib\\|^rdma\\)' <(%s lsmod)", chrootDefinition)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "RdmaIsLoaded(): fail to check if ib and rdma kernel modules are loaded", "stderr", stderr.String()) - return false, fmt.Errorf(stderr.String()) - } - - if err != nil { - return false, nil - } - - return true, nil -} - -func (h *HostManager) EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) { - path := conditionFilePath - if !h.RunOnHost { - path = pathlib.Join(hostPathFromDaemon, path) - } - log.Log.Info("EnableRDMA(): checking for service file", "path", path) - - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - log.Log.V(2).Info("EnableRDMA(): RDMA server doesn't exist") - err = h.InstallRDMA(packageManager) - if err != nil { - log.Log.Error(err, "EnableRDMA() failed to install RDMA package") - return false, err - } - - err = h.TriggerUdevEvent() - if err != nil { - log.Log.Error(err, "EnableRDMA() failed to trigger udev event") - return false, err - } - - return false, nil - } - - log.Log.Error(err, "EnableRDMA() failed to check for os release file", "path", path) - return false, err - } - - log.Log.Info("EnableRDMA(): service installed", "name", serviceName) - return true, nil -} - -func (h *HostManager) InstallRDMA(packageManager string) error { - log.Log.Info("InstallRDMA(): installing RDMA") - chrootDefinition := getChrootExtension(h.RunOnHost) - - stdout, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("%s %s install -y rdma-core", chrootDefinition, packageManager)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "InstallRDMA(): failed to install RDMA package", "stdout", stdout.String(), "stderr", stderr.String()) - return err - } - - return nil -} - -func (h *HostManager) TriggerUdevEvent() error { - log.Log.Info("TriggerUdevEvent(): installing RDMA") - - err := h.ReloadDriver("mlx4_en") - if err != nil { - return err - } - - err = h.ReloadDriver("mlx5_core") - if err != nil { - return err - } - - return nil -} - -func (h *HostManager) ReloadDriver(driverName string) error { - log.Log.Info("ReloadDriver(): reload driver", "name", driverName) - chrootDefinition := getChrootExtension(h.RunOnHost) - - _, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("%s modprobe -r %s && %s modprobe %s", chrootDefinition, driverName, chrootDefinition, driverName)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "InstallRDMA(): failed to reload kernel module", - "name", driverName, "stderr", stderr.String()) - return err - } - - return nil -} - -func (h *HostManager) GetOSPrettyName() (string, error) { - path := genericOSReleaseFile - if !h.RunOnHost { - path = pathlib.Join(hostPathFromDaemon, path) - } - - log.Log.Info("GetOSPrettyName(): getting os name from os-release file") - - stdout, stderr, err := h.cmd.Run("/bin/sh", "-c", fmt.Sprintf("cat %s | grep PRETTY_NAME | cut -c 13-", path)) - if err != nil && stderr.Len() != 0 { - log.Log.Error(err, "IsUbuntuSystem(): failed to check for ubuntu operating system name in os-releasae file", "stderr", stderr.String()) - return "", fmt.Errorf(stderr.String()) - } - - if stdout.Len() > 0 { - return stdout.String(), nil - } - - return "", fmt.Errorf("failed to find pretty operating system name") -} - -func getChrootExtension(runOnHost bool) string { - if !runOnHost { - return fmt.Sprintf("chroot %s/host", utils.FilesystemRoot) - } - return utils.FilesystemRoot -} diff --git a/pkg/host/internal/common.go b/pkg/host/internal/common.go new file mode 100644 index 000000000..b32d2cd93 --- /dev/null +++ b/pkg/host/internal/common.go @@ -0,0 +1,19 @@ +package internal + +import ( + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" +) + +const ( + HostPathFromDaemon = consts.Host + RedhatReleaseFile = "/etc/redhat-release" + RhelRDMAConditionFile = "/usr/libexec/rdma-init-kernel" + RhelRDMAServiceName = "rdma" + RhelPackageManager = "yum" + + UbuntuRDMAConditionFile = "/usr/sbin/rdma-ndd" + UbuntuRDMAServiceName = "rdma-ndd" + UbuntuPackageManager = "apt-get" + + GenericOSReleaseFile = "/etc/os-release" +) diff --git a/pkg/host/internal/kernel/kernel.go b/pkg/host/internal/kernel/kernel.go new file mode 100644 index 000000000..3bb539374 --- /dev/null +++ b/pkg/host/internal/kernel/kernel.go @@ -0,0 +1,661 @@ +package kernel + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "sigs.k8s.io/controller-runtime/pkg/log" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" +) + +type kernel struct { + utilsHelper utils.CmdInterface +} + +func New(utilsHelper utils.CmdInterface) types.KernelInterface { + return &kernel{utilsHelper: utilsHelper} +} + +func (k *kernel) LoadKernelModule(name string, args ...string) error { + log.Log.Info("LoadKernelModule(): try to load kernel module", "name", name, "args", args) + chrootDefinition := utils.GetChrootExtension() + cmdArgs := strings.Join(args, " ") + + // check if the driver is already loaded in to the system + isLoaded, err := k.IsKernelModuleLoaded(name) + if err != nil { + log.Log.Error(err, "LoadKernelModule(): failed to check if kernel module is already loaded", "name", name) + } + if isLoaded { + log.Log.Info("LoadKernelModule(): kernel module already loaded", "name", name) + return nil + } + + _, _, err = k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s modprobe %s %s", chrootDefinition, name, cmdArgs)) + if err != nil { + log.Log.Error(err, "LoadKernelModule(): failed to load kernel module with arguments", "name", name, "args", args) + return err + } + return nil +} + +func (k *kernel) IsKernelModuleLoaded(kernelModuleName string) (bool, error) { + log.Log.Info("IsKernelModuleLoaded(): check if kernel module is loaded", "name", kernelModuleName) + chrootDefinition := utils.GetChrootExtension() + + stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s lsmod | grep \"^%s\"", chrootDefinition, kernelModuleName)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", + "name", kernelModuleName, "stderr", stderr) + return false, err + } + log.Log.V(2).Info("IsKernelModuleLoaded():", "stdout", stdout) + if len(stderr) != 0 { + log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", "name", kernelModuleName, "stderr", stderr) + return false, fmt.Errorf(stderr) + } + + if len(stdout) != 0 { + log.Log.Info("IsKernelModuleLoaded(): kernel module already loaded", "name", kernelModuleName) + return true, nil + } + + return false, nil +} + +func (k *kernel) TryEnableTun() { + if err := k.LoadKernelModule("tun"); err != nil { + log.Log.Error(err, "tryEnableTun(): TUN kernel module not loaded") + } +} + +func (k *kernel) TryEnableVhostNet() { + if err := k.LoadKernelModule("vhost_net"); err != nil { + log.Log.Error(err, "tryEnableVhostNet(): VHOST_NET kernel module not loaded") + } +} + +// GetCurrentKernelArgs This retrieves the kernel cmd line arguments +func (k *kernel) GetCurrentKernelArgs() (string, error) { + path := consts.ProcKernelCmdLine + if !vars.UsingSystemdMode { + path = filepath.Join(consts.Host, path) + } + + path = filepath.Join(vars.FilesystemRoot, path) + cmdLine, err := os.ReadFile(path) + if err != nil { + return "", fmt.Errorf("GetCurrentKernelArgs(): Error reading %s: %v", path, err) + } + return string(cmdLine), nil +} + +// IsKernelArgsSet This checks if the kernel cmd line is set properly. Please note that the same key could be repeated +// several times in the kernel cmd line. We can only ensure that the kernel cmd line has the key/val kernel arg that we set. +func (k *kernel) IsKernelArgsSet(cmdLine string, karg string) bool { + elements := strings.Fields(cmdLine) + for _, element := range elements { + if element == karg { + return true + } + } + return false +} + +// Unbind unbind driver for one device +func (k *kernel) Unbind(pciAddr string) error { + log.Log.V(2).Info("Unbind(): unbind device driver for device", "device", pciAddr) + return k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr) +} + +// BindDpdkDriver bind dpdk driver for one device +// Bind the device given by "pciAddr" to the driver "driver" +func (k *kernel) BindDpdkDriver(pciAddr, driver string) error { + log.Log.V(2).Info("BindDpdkDriver(): bind device to driver", + "device", pciAddr, "driver", driver) + if err := k.BindDriverByBusAndDevice(consts.BusPci, pciAddr, driver); err != nil { + _, innerErr := os.Readlink(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "iommu_group")) + if innerErr != nil { + log.Log.Error(err, "Could not read IOMMU group for device", "device", pciAddr) + return fmt.Errorf( + "cannot bind driver %s to device %s, make sure IOMMU is enabled in BIOS. %w", driver, pciAddr, innerErr) + } + return err + } + return nil +} + +// BindDefaultDriver bind driver for one device +// Bind the device given by "pciAddr" to the default driver +func (k *kernel) BindDefaultDriver(pciAddr string) error { + log.Log.V(2).Info("BindDefaultDriver(): bind device to default driver", "device", pciAddr) + + curDriver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) + if err != nil { + return err + } + if curDriver != "" { + if !sriovnetworkv1.StringInArray(curDriver, vars.DpdkDrivers) { + log.Log.V(2).Info("BindDefaultDriver(): device already bound to default driver", + "device", pciAddr, "driver", curDriver) + return nil + } + if err := k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr); err != nil { + return err + } + } + if err := setDriverOverride(consts.BusPci, pciAddr, ""); err != nil { + return err + } + if err := probeDriver(consts.BusPci, pciAddr); err != nil { + return err + } + return nil +} + +// BindDriverByBusAndDevice binds device to the provided driver +// bus - the bus path in the sysfs, e.g. "pci" or "vdpa" +// device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA +// driver - the name of the driver, e.g. vfio-pci or vhost_vdpa. +func (k *kernel) BindDriverByBusAndDevice(bus, device, driver string) error { + log.Log.V(2).Info("BindDriverByBusAndDevice(): bind device to driver", + "bus", bus, "device", device, "driver", driver) + + curDriver, err := getDriverByBusAndDevice(bus, device) + if err != nil { + return err + } + if curDriver != "" { + if curDriver == driver { + log.Log.V(2).Info("BindDriverByBusAndDevice(): device already bound to driver", + "bus", bus, "device", device, "driver", driver) + return nil + } + if err := k.UnbindDriverByBusAndDevice(bus, device); err != nil { + return err + } + } + if err := setDriverOverride(bus, device, driver); err != nil { + return err + } + if err := bindDriver(bus, device, driver); err != nil { + return err + } + return setDriverOverride(bus, device, "") +} + +// Workaround function to handle a case where the vf default driver is stuck and not able to create the vf kernel interface. +// This function unbind the VF from the default driver and try to bind it again +// bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2045087 +func (k *kernel) RebindVfToDefaultDriver(vfAddr string) error { + log.Log.Info("RebindVfToDefaultDriver()", "vf", vfAddr) + if err := k.Unbind(vfAddr); err != nil { + return err + } + if err := k.BindDefaultDriver(vfAddr); err != nil { + log.Log.Error(err, "RebindVfToDefaultDriver(): fail to bind default driver", "device", vfAddr) + return err + } + + log.Log.Info("RebindVfToDefaultDriver(): workaround implemented", "vf", vfAddr) + return nil +} + +func (k *kernel) UnbindDriverIfNeeded(vfAddr string, isRdma bool) error { + if isRdma { + log.Log.Info("UnbindDriverIfNeeded(): unbinding driver", "device", vfAddr) + if err := k.Unbind(vfAddr); err != nil { + return err + } + log.Log.Info("UnbindDriverIfNeeded(): unbounded driver", "device", vfAddr) + } + return nil +} + +// UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver +// bus - the bus path in the sysfs, e.g. "pci" or "vdpa" +// device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA +func (k *kernel) UnbindDriverByBusAndDevice(bus, device string) error { + log.Log.V(2).Info("UnbindDriverByBusAndDevice(): unbind device driver for device", "bus", bus, "device", device) + driver, err := getDriverByBusAndDevice(bus, device) + if err != nil { + return err + } + if driver == "" { + log.Log.V(2).Info("UnbindDriverByBusAndDevice(): device has no driver", "bus", bus, "device", device) + return nil + } + return unbindDriver(bus, device, driver) +} + +func (k *kernel) HasDriver(pciAddr string) (bool, string) { + driver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) + if err != nil { + log.Log.V(2).Info("HasDriver(): device driver is empty for device", "device", pciAddr) + return false, "" + } + if driver != "" { + log.Log.V(2).Info("HasDriver(): device driver for device", "device", pciAddr, "driver", driver) + return true, driver + } + return false, "" +} + +func (k *kernel) TryEnableRdma() (bool, error) { + log.Log.V(2).Info("tryEnableRdma()") + chrootDefinition := utils.GetChrootExtension() + + // check if the driver is already loaded in to the system + _, stderr, mlx4Err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx4_en' <(%s lsmod)", chrootDefinition)) + if mlx4Err != nil && len(stderr) != 0 { + log.Log.Error(mlx4Err, "tryEnableRdma(): failed to check for kernel module 'mlx4_en'", "stderr", stderr) + return false, fmt.Errorf(stderr) + } + + _, stderr, mlx5Err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx5_core' <(%s lsmod)", chrootDefinition)) + if mlx5Err != nil && len(stderr) != 0 { + log.Log.Error(mlx5Err, "tryEnableRdma(): failed to check for kernel module 'mlx5_core'", "stderr", stderr) + return false, fmt.Errorf(stderr) + } + + if mlx4Err != nil && mlx5Err != nil { + log.Log.Error(nil, "tryEnableRdma(): no RDMA capable devices") + return false, nil + } + + isRhelSystem, err := k.IsRHELSystem() + if err != nil { + log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on RHEL") + return false, err + } + + // RHEL check + if isRhelSystem { + return k.EnableRDMAOnRHELMachine() + } + + isUbuntuSystem, err := k.IsUbuntuSystem() + if err != nil { + log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on Ubuntu") + return false, err + } + + if isUbuntuSystem { + return k.EnableRDMAOnUbuntuMachine() + } + + osName, err := k.GetOSPrettyName() + if err != nil { + log.Log.Error(err, "tryEnableRdma(): failed to check OS name") + return false, err + } + + log.Log.Error(nil, "tryEnableRdma(): Unsupported OS", "name", osName) + return false, fmt.Errorf("unable to load RDMA unsupported OS: %s", osName) +} + +func (k *kernel) EnableRDMAOnRHELMachine() (bool, error) { + log.Log.Info("EnableRDMAOnRHELMachine()") + isCoreOsSystem, err := k.IsCoreOS() + if err != nil { + log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if the machine runs CoreOS") + return false, err + } + + // CoreOS check + if isCoreOsSystem { + isRDMALoaded, err := k.RdmaIsLoaded() + if err != nil { + log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") + return false, err + } + + return isRDMALoaded, nil + } + + // RHEL + log.Log.Info("EnableRDMAOnRHELMachine(): enabling RDMA on RHEL machine") + isRDMAEnable, err := k.EnableRDMA(internal.RhelRDMAConditionFile, internal.RhelRDMAServiceName, internal.RhelPackageManager) + if err != nil { + log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to enable RDMA on RHEL machine") + return false, err + } + + // check if we need to install rdma-core package + if isRDMAEnable { + isRDMALoaded, err := k.RdmaIsLoaded() + if err != nil { + log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") + return false, err + } + + // if ib kernel module is not loaded trigger a loading + if isRDMALoaded { + err = k.TriggerUdevEvent() + if err != nil { + log.Log.Error(err, "EnableRDMAOnRHELMachine() failed to trigger udev event") + return false, err + } + } + } + + return true, nil +} + +func (k *kernel) EnableRDMAOnUbuntuMachine() (bool, error) { + log.Log.Info("EnableRDMAOnUbuntuMachine(): enabling RDMA on RHEL machine") + isRDMAEnable, err := k.EnableRDMA(internal.UbuntuRDMAConditionFile, internal.UbuntuRDMAServiceName, internal.UbuntuPackageManager) + if err != nil { + log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to enable RDMA on Ubuntu machine") + return false, err + } + + // check if we need to install rdma-core package + if isRDMAEnable { + isRDMALoaded, err := k.RdmaIsLoaded() + if err != nil { + log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to check if RDMA kernel modules are loaded") + return false, err + } + + // if ib kernel module is not loaded trigger a loading + if isRDMALoaded { + err = k.TriggerUdevEvent() + if err != nil { + log.Log.Error(err, "EnableRDMAOnUbuntuMachine() failed to trigger udev event") + return false, err + } + } + } + + return true, nil +} + +func (k *kernel) IsRHELSystem() (bool, error) { + log.Log.Info("IsRHELSystem(): checking for RHEL machine") + path := internal.RedhatReleaseFile + if !vars.UsingSystemdMode { + path = filepath.Join(internal.HostPathFromDaemon, path) + } + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + log.Log.V(2).Info("IsRHELSystem() not a RHEL machine") + return false, nil + } + + log.Log.Error(err, "IsRHELSystem() failed to check for os release file", "path", path) + return false, err + } + + return true, nil +} + +func (k *kernel) IsCoreOS() (bool, error) { + log.Log.Info("IsCoreOS(): checking for CoreOS machine") + path := internal.RedhatReleaseFile + if !vars.UsingSystemdMode { + path = filepath.Join(internal.HostPathFromDaemon, path) + } + + data, err := os.ReadFile(path) + if err != nil { + log.Log.Error(err, "IsCoreOS(): failed to read RHEL release file on path", "path", path) + return false, err + } + + if strings.Contains(string(data), "CoreOS") { + return true, nil + } + + return false, nil +} + +func (k *kernel) IsUbuntuSystem() (bool, error) { + log.Log.Info("IsUbuntuSystem(): checking for Ubuntu machine") + path := internal.GenericOSReleaseFile + if !vars.UsingSystemdMode { + path = filepath.Join(internal.HostPathFromDaemon, path) + } + + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + log.Log.Error(nil, "IsUbuntuSystem() os-release on path doesn't exist", "path", path) + return false, err + } + + log.Log.Error(err, "IsUbuntuSystem() failed to check for os release file", "path", path) + return false, err + } + + stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep -i --quiet 'ubuntu' %s", path)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "IsUbuntuSystem(): failed to check for ubuntu operating system name in os-releasae file", "stderr", stderr) + return false, fmt.Errorf(stderr) + } + + if len(stdout) > 0 { + return true, nil + } + + return false, nil +} + +func (k *kernel) RdmaIsLoaded() (bool, error) { + log.Log.V(2).Info("RdmaIsLoaded()") + chrootDefinition := utils.GetChrootExtension() + + // check if the driver is already loaded in to the system + _, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet '\\(^ib\\|^rdma\\)' <(%s lsmod)", chrootDefinition)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "RdmaIsLoaded(): fail to check if ib and rdma kernel modules are loaded", "stderr", stderr) + return false, fmt.Errorf(stderr) + } + + if err != nil { + return false, nil + } + + return true, nil +} + +func (k *kernel) EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) { + path := conditionFilePath + if !vars.UsingSystemdMode { + path = filepath.Join(internal.HostPathFromDaemon, path) + } + log.Log.Info("EnableRDMA(): checking for service file", "path", path) + + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + log.Log.V(2).Info("EnableRDMA(): RDMA server doesn't exist") + err = k.InstallRDMA(packageManager) + if err != nil { + log.Log.Error(err, "EnableRDMA() failed to install RDMA package") + return false, err + } + + err = k.TriggerUdevEvent() + if err != nil { + log.Log.Error(err, "EnableRDMA() failed to trigger udev event") + return false, err + } + + return false, nil + } + + log.Log.Error(err, "EnableRDMA() failed to check for os release file", "path", path) + return false, err + } + + log.Log.Info("EnableRDMA(): service installed", "name", serviceName) + return true, nil +} + +func (k *kernel) InstallRDMA(packageManager string) error { + log.Log.Info("InstallRDMA(): installing RDMA") + chrootDefinition := utils.GetChrootExtension() + + stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s %s install -y rdma-core", chrootDefinition, packageManager)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "InstallRDMA(): failed to install RDMA package", "stdout", stdout, "stderr", stderr) + return err + } + + return nil +} + +func (k *kernel) TriggerUdevEvent() error { + log.Log.Info("TriggerUdevEvent(): installing RDMA") + + err := k.ReloadDriver("mlx4_en") + if err != nil { + return err + } + + err = k.ReloadDriver("mlx5_core") + if err != nil { + return err + } + + return nil +} + +func (k *kernel) ReloadDriver(driverName string) error { + log.Log.Info("ReloadDriver(): reload driver", "name", driverName) + chrootDefinition := utils.GetChrootExtension() + + _, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s modprobe -r %s && %s modprobe %s", chrootDefinition, driverName, chrootDefinition, driverName)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "ReloadDriver(): failed to reload kernel module", + "name", driverName, "stderr", stderr) + return err + } + + return nil +} + +func (k *kernel) GetOSPrettyName() (string, error) { + path := internal.GenericOSReleaseFile + if !vars.UsingSystemdMode { + path = filepath.Join(internal.HostPathFromDaemon, path) + } + + log.Log.Info("GetOSPrettyName(): getting os name from os-release file") + + stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("cat %s | grep PRETTY_NAME | cut -c 13-", path)) + if err != nil && len(stderr) != 0 { + log.Log.Error(err, "GetOSPrettyName(): failed to check for operating system name in os-release file", "stderr", stderr) + return "", fmt.Errorf(stderr) + } + + if len(stdout) > 0 { + return stdout, nil + } + + return "", fmt.Errorf("failed to find pretty operating system name") +} + +// IsKernelLockdownMode returns true when kernel lockdown mode is enabled +// TODO: change this to return error +func (k *kernel) IsKernelLockdownMode() bool { + path := utils.GetHostExtension() + path = filepath.Join(path, "/sys/kernel/security/lockdown") + + stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", "cat", path) + log.Log.V(2).Info("IsKernelLockdownMode()", "output", stdout, "error", err) + if err != nil { + log.Log.Error(err, "IsKernelLockdownMode(): failed to check for lockdown file", "stderr", stderr) + return false + } + return strings.Contains(stdout, "[integrity]") || strings.Contains(stdout, "[confidentiality]") +} + +// returns driver for device on the bus +func getDriverByBusAndDevice(bus, device string) (string, error) { + driverLink := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver") + driverInfo, err := os.Readlink(driverLink) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + log.Log.V(2).Info("getDriverByBusAndDevice(): driver path for device not exist", "bus", bus, "device", device, "driver", driverInfo) + return "", nil + } + log.Log.Error(err, "getDriverByBusAndDevice(): error getting driver info for device", "bus", bus, "device", device) + return "", err + } + log.Log.V(2).Info("getDriverByBusAndDevice(): driver for device", "bus", bus, "device", device, "driver", driverInfo) + return filepath.Base(driverInfo), nil +} + +// binds device to the provide driver +func bindDriver(bus, device, driver string) error { + log.Log.V(2).Info("bindDriver(): bind to driver", "bus", bus, "device", device, "driver", driver) + bindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "bind") + err := os.WriteFile(bindPath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "bindDriver(): failed to bind driver", "bus", bus, "device", device, "driver", driver) + return err + } + return nil +} + +// unbind device from the driver +func unbindDriver(bus, device, driver string) error { + log.Log.V(2).Info("unbindDriver(): unbind from driver", "bus", bus, "device", device, "driver", driver) + unbindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "unbind") + err := os.WriteFile(unbindPath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "unbindDriver(): failed to unbind driver", "bus", bus, "device", device, "driver", driver) + return err + } + return nil +} + +// probes driver for device on the bus +func probeDriver(bus, device string) error { + log.Log.V(2).Info("probeDriver(): drivers probe", "bus", bus, "device", device) + probePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers_probe") + err := os.WriteFile(probePath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "probeDriver(): failed to trigger driver probe", "bus", bus, "device", device) + return err + } + return nil +} + +// set driver override for the bus/device, +// resets override if override arg is "", +// if device doesn't support overriding (has no driver_override path), does nothing +func setDriverOverride(bus, device, override string) error { + driverOverridePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver_override") + if _, err := os.Stat(driverOverridePath); err != nil { + if os.IsNotExist(err) { + log.Log.V(2).Info("setDriverOverride(): device doesn't support driver override, skip", "bus", bus, "device", device) + return nil + } + return err + } + var overrideData []byte + if override != "" { + log.Log.V(2).Info("setDriverOverride(): configure driver override for device", "bus", bus, "device", device, "driver", override) + overrideData = []byte(override) + } else { + log.Log.V(2).Info("setDriverOverride(): reset driver override for device", "bus", bus, "device", device) + overrideData = []byte("\x00") + } + err := os.WriteFile(driverOverridePath, overrideData, os.ModeAppend) + if err != nil { + log.Log.Error(err, "setDriverOverride(): fail to write driver_override for device", + "bus", bus, "device", device, "driver", override) + return err + } + return nil +} diff --git a/pkg/host/internal/kernel/kernel_test.go b/pkg/host/internal/kernel/kernel_test.go new file mode 100644 index 000000000..b8eef8b07 --- /dev/null +++ b/pkg/host/internal/kernel/kernel_test.go @@ -0,0 +1,199 @@ +package kernel + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/helpers" +) + +var _ = Describe("Kernel", func() { + Context("Drivers", func() { + var ( + k types.KernelInterface + ) + BeforeEach(func() { + k = New(nil) + }) + Context("Unbind, UnbindDriverByBusAndDevice", func() { + It("unknown device", func() { + Expect(k.UnbindDriverByBusAndDevice(consts.BusPci, "unknown-dev")).NotTo(HaveOccurred()) + }) + It("known device, no driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{Dirs: []string{"/sys/bus/pci/devices/0000:d8:00.0"}}) + Expect(k.Unbind("0000:d8:00.0")).NotTo(HaveOccurred()) + }) + It("has driver, succeed", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}}, + }) + Expect(k.Unbind("0000:d8:00.0")).NotTo(HaveOccurred()) + // check that echo to unbind path was done + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + }) + It("has driver, failed to unbind", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + Expect(k.Unbind("0000:d8:00.0")).To(HaveOccurred()) + }) + }) + Context("HasDriver", func() { + It("unknown device", func() { + has, driver := k.HasDriver("unknown-dev") + Expect(has).To(BeFalse()) + Expect(driver).To(BeEmpty()) + }) + It("known device, no driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{Dirs: []string{"/sys/bus/pci/devices/0000:d8:00.0"}}) + has, driver := k.HasDriver("0000:d8:00.0") + Expect(has).To(BeFalse()) + Expect(driver).To(BeEmpty()) + }) + It("has driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + has, driver := k.HasDriver("0000:d8:00.0") + Expect(has).To(BeTrue()) + Expect(driver).To(Equal("test-driver")) + }) + }) + Context("BindDefaultDriver", func() { + It("unknown device", func() { + Expect(k.BindDefaultDriver("unknown-dev")).To(HaveOccurred()) + }) + It("no driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers_probe": {}, "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + // should probe driver for dev + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers_probe", "0000:d8:00.0") + }) + It("already bind to default driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + }) + It("bind to dpdk driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/vfio-pci"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers_probe": {}, + "/sys/bus/pci/drivers/vfio-pci/unbind": {}}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + // should unbind from dpdk driver + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/unbind", "0000:d8:00.0") + // should probe driver for dev + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers_probe", "0000:d8:00.0") + }) + }) + Context("BindDpdkDriver", func() { + It("unknown device", func() { + Expect(k.BindDpdkDriver("unknown-dev", "vfio-pci")).To(HaveOccurred()) + }) + It("no driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/vfio-pci"}, + Files: map[string][]byte{ + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should reset driver override + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/devices/0000:d8:00.0/driver_override", "\x00") + }) + It("already bind to required driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/vfio-pci"}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + }) + It("bind to wrong driver", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/drivers/vfio-pci/bind": {}, + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should unbind from driver1 + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + // should bind to driver2 + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/bind", "0000:d8:00.0") + }) + It("fail to bind", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).To(HaveOccurred()) + }) + }) + Context("BindDriverByBusAndDevice", func() { + It("device doesn't support driver_override", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/drivers/vfio-pci/bind": {}}, + }) + Expect(k.BindDriverByBusAndDevice(consts.BusPci, "0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should unbind from driver1 + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + // should bind to driver2 + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/bind", "0000:d8:00.0") + }) + }) + }) +}) diff --git a/pkg/host/internal/kernel/suite_test.go b/pkg/host/internal/kernel/suite_test.go new file mode 100644 index 000000000..b36acb454 --- /dev/null +++ b/pkg/host/internal/kernel/suite_test.go @@ -0,0 +1,21 @@ +package kernel + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +func TestKernel(t *testing.T) { + log.SetLogger(zap.New( + zap.WriteTo(GinkgoWriter), + zap.Level(zapcore.Level(-2)), + zap.UseDevMode(true))) + RegisterFailHandler(Fail) + RunSpecs(t, "Package Kernel Suite") +} diff --git a/pkg/host/internal/lib/dputils/dputils.go b/pkg/host/internal/lib/dputils/dputils.go new file mode 100644 index 000000000..ef503a851 --- /dev/null +++ b/pkg/host/internal/lib/dputils/dputils.go @@ -0,0 +1,78 @@ +package dputils + +import ( + dputils "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" +) + +func New() DPUtilsLib { + return &libWrapper{} +} + +//go:generate ../../../../../bin/mockgen -destination mock/mock_dputils.go -source dputils.go +type DPUtilsLib interface { + // GetNetNames returns host net interface names as string for a PCI device from its pci address + GetNetNames(pciAddr string) ([]string, error) + // GetDriverName returns current driver attached to a pci device from its pci address + GetDriverName(pciAddr string) (string, error) + // GetVFID returns VF ID index (within specific PF) based on PCI address + GetVFID(pciAddr string) (vfID int, err error) + // IsSriovVF check if a pci device has link to a PF + IsSriovVF(pciAddr string) bool + // IsSriovPF check if a pci device SRIOV capable given its pci address + IsSriovPF(pciAddr string) bool + // GetSriovVFcapacity returns SRIOV VF capacity + GetSriovVFcapacity(pf string) int + // GetVFconfigured returns number of VF configured for a PF + GetVFconfigured(pf string) int + // SriovConfigured returns true if sriov_numvfs reads > 0 else false + SriovConfigured(addr string) bool + // GetVFList returns a List containing PCI addr for all VF discovered in a given PF + GetVFList(pf string) (vfList []string, err error) +} + +type libWrapper struct{} + +// GetNetNames returns host net interface names as string for a PCI device from its pci address +func (w *libWrapper) GetNetNames(pciAddr string) ([]string, error) { + return dputils.GetNetNames(pciAddr) +} + +// GetDriverName returns current driver attached to a pci device from its pci address +func (w *libWrapper) GetDriverName(pciAddr string) (string, error) { + return dputils.GetDriverName(pciAddr) +} + +// GetVFID returns VF ID index (within specific PF) based on PCI address +func (w *libWrapper) GetVFID(pciAddr string) (vfID int, err error) { + return dputils.GetVFID(pciAddr) +} + +// IsSriovVF check if a pci device has link to a PF +func (w *libWrapper) IsSriovVF(pciAddr string) bool { + return dputils.IsSriovVF(pciAddr) +} + +// IsSriovPF check if a pci device SRIOV capable given its pci address +func (w *libWrapper) IsSriovPF(pciAddr string) bool { + return dputils.IsSriovPF(pciAddr) +} + +// GetSriovVFcapacity returns SRIOV VF capacity +func (w *libWrapper) GetSriovVFcapacity(pf string) int { + return dputils.GetSriovVFcapacity(pf) +} + +// GetVFconfigured returns number of VF configured for a PF +func (w *libWrapper) GetVFconfigured(pf string) int { + return dputils.GetVFconfigured(pf) +} + +// SriovConfigured returns true if sriov_numvfs reads > 0 else false +func (w *libWrapper) SriovConfigured(addr string) bool { + return dputils.SriovConfigured(addr) +} + +// GetVFList returns a List containing PCI addr for all VF discovered in a given PF +func (w *libWrapper) GetVFList(pf string) (vfList []string, err error) { + return dputils.GetVFList(pf) +} diff --git a/pkg/host/internal/lib/dputils/mock/mock_dputils.go b/pkg/host/internal/lib/dputils/mock/mock_dputils.go new file mode 100644 index 000000000..de32180f2 --- /dev/null +++ b/pkg/host/internal/lib/dputils/mock/mock_dputils.go @@ -0,0 +1,164 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: dputils.go + +// Package mock_dputils is a generated GoMock package. +package mock_dputils + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockDPUtilsLib is a mock of DPUtilsLib interface. +type MockDPUtilsLib struct { + ctrl *gomock.Controller + recorder *MockDPUtilsLibMockRecorder +} + +// MockDPUtilsLibMockRecorder is the mock recorder for MockDPUtilsLib. +type MockDPUtilsLibMockRecorder struct { + mock *MockDPUtilsLib +} + +// NewMockDPUtilsLib creates a new mock instance. +func NewMockDPUtilsLib(ctrl *gomock.Controller) *MockDPUtilsLib { + mock := &MockDPUtilsLib{ctrl: ctrl} + mock.recorder = &MockDPUtilsLibMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDPUtilsLib) EXPECT() *MockDPUtilsLibMockRecorder { + return m.recorder +} + +// GetDriverName mocks base method. +func (m *MockDPUtilsLib) GetDriverName(pciAddr string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDriverName", pciAddr) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDriverName indicates an expected call of GetDriverName. +func (mr *MockDPUtilsLibMockRecorder) GetDriverName(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDriverName", reflect.TypeOf((*MockDPUtilsLib)(nil).GetDriverName), pciAddr) +} + +// GetNetNames mocks base method. +func (m *MockDPUtilsLib) GetNetNames(pciAddr string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetNames", pciAddr) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNetNames indicates an expected call of GetNetNames. +func (mr *MockDPUtilsLibMockRecorder) GetNetNames(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetNames", reflect.TypeOf((*MockDPUtilsLib)(nil).GetNetNames), pciAddr) +} + +// GetSriovVFcapacity mocks base method. +func (m *MockDPUtilsLib) GetSriovVFcapacity(pf string) int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSriovVFcapacity", pf) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetSriovVFcapacity indicates an expected call of GetSriovVFcapacity. +func (mr *MockDPUtilsLibMockRecorder) GetSriovVFcapacity(pf interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSriovVFcapacity", reflect.TypeOf((*MockDPUtilsLib)(nil).GetSriovVFcapacity), pf) +} + +// GetVFID mocks base method. +func (m *MockDPUtilsLib) GetVFID(pciAddr string) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVFID", pciAddr) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVFID indicates an expected call of GetVFID. +func (mr *MockDPUtilsLibMockRecorder) GetVFID(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVFID", reflect.TypeOf((*MockDPUtilsLib)(nil).GetVFID), pciAddr) +} + +// GetVFList mocks base method. +func (m *MockDPUtilsLib) GetVFList(pf string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVFList", pf) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVFList indicates an expected call of GetVFList. +func (mr *MockDPUtilsLibMockRecorder) GetVFList(pf interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVFList", reflect.TypeOf((*MockDPUtilsLib)(nil).GetVFList), pf) +} + +// GetVFconfigured mocks base method. +func (m *MockDPUtilsLib) GetVFconfigured(pf string) int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVFconfigured", pf) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetVFconfigured indicates an expected call of GetVFconfigured. +func (mr *MockDPUtilsLibMockRecorder) GetVFconfigured(pf interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVFconfigured", reflect.TypeOf((*MockDPUtilsLib)(nil).GetVFconfigured), pf) +} + +// IsSriovPF mocks base method. +func (m *MockDPUtilsLib) IsSriovPF(pciAddr string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsSriovPF", pciAddr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsSriovPF indicates an expected call of IsSriovPF. +func (mr *MockDPUtilsLibMockRecorder) IsSriovPF(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSriovPF", reflect.TypeOf((*MockDPUtilsLib)(nil).IsSriovPF), pciAddr) +} + +// IsSriovVF mocks base method. +func (m *MockDPUtilsLib) IsSriovVF(pciAddr string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsSriovVF", pciAddr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsSriovVF indicates an expected call of IsSriovVF. +func (mr *MockDPUtilsLibMockRecorder) IsSriovVF(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSriovVF", reflect.TypeOf((*MockDPUtilsLib)(nil).IsSriovVF), pciAddr) +} + +// SriovConfigured mocks base method. +func (m *MockDPUtilsLib) SriovConfigured(addr string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SriovConfigured", addr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// SriovConfigured indicates an expected call of SriovConfigured. +func (mr *MockDPUtilsLibMockRecorder) SriovConfigured(addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SriovConfigured", reflect.TypeOf((*MockDPUtilsLib)(nil).SriovConfigured), addr) +} diff --git a/pkg/host/internal/lib/govdpa/govdpa.go b/pkg/host/internal/lib/govdpa/govdpa.go new file mode 100644 index 000000000..e85f89db1 --- /dev/null +++ b/pkg/host/internal/lib/govdpa/govdpa.go @@ -0,0 +1,40 @@ +package govdpa + +import ( + "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" +) + +func New() GoVdpaLib { + return &libWrapper{} +} + +type VdpaDevice interface { + kvdpa.VdpaDevice +} + +//go:generate ../../../../../bin/mockgen -destination mock/mock_govdpa.go -source govdpa.go +type GoVdpaLib interface { + // GetVdpaDevice returns the vdpa device information by a vdpa device name + GetVdpaDevice(vdpaDeviceName string) (VdpaDevice, error) + // AddVdpaDevice adds a new vdpa device to the given management device + AddVdpaDevice(mgmtDeviceName string, vdpaDeviceName string) error + // DeleteVdpaDevice deletes a vdpa device + DeleteVdpaDevice(vdpaDeviceName string) error +} + +type libWrapper struct{} + +// GetVdpaDevice returns the vdpa device information by a vdpa device name +func (w *libWrapper) GetVdpaDevice(name string) (VdpaDevice, error) { + return kvdpa.GetVdpaDevice(name) +} + +// AddVdpaDevice adds a new vdpa device to the given management device +func (w *libWrapper) AddVdpaDevice(mgmtDeviceName string, vdpaDeviceName string) error { + return kvdpa.AddVdpaDevice(mgmtDeviceName, vdpaDeviceName) +} + +// DeleteVdpaDevice deletes a vdpa device +func (w *libWrapper) DeleteVdpaDevice(name string) error { + return kvdpa.DeleteVdpaDevice(name) +} diff --git a/pkg/host/internal/lib/govdpa/mock/mock_govdpa.go b/pkg/host/internal/lib/govdpa/mock/mock_govdpa.go new file mode 100644 index 000000000..cdeeac743 --- /dev/null +++ b/pkg/host/internal/lib/govdpa/mock/mock_govdpa.go @@ -0,0 +1,187 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: govdpa.go + +// Package mock_govdpa is a generated GoMock package. +package mock_govdpa + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + kvdpa "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" + govdpa "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/govdpa" +) + +// MockVdpaDevice is a mock of VdpaDevice interface. +type MockVdpaDevice struct { + ctrl *gomock.Controller + recorder *MockVdpaDeviceMockRecorder +} + +// MockVdpaDeviceMockRecorder is the mock recorder for MockVdpaDevice. +type MockVdpaDeviceMockRecorder struct { + mock *MockVdpaDevice +} + +// NewMockVdpaDevice creates a new mock instance. +func NewMockVdpaDevice(ctrl *gomock.Controller) *MockVdpaDevice { + mock := &MockVdpaDevice{ctrl: ctrl} + mock.recorder = &MockVdpaDeviceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVdpaDevice) EXPECT() *MockVdpaDeviceMockRecorder { + return m.recorder +} + +// Driver mocks base method. +func (m *MockVdpaDevice) Driver() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Driver") + ret0, _ := ret[0].(string) + return ret0 +} + +// Driver indicates an expected call of Driver. +func (mr *MockVdpaDeviceMockRecorder) Driver() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Driver", reflect.TypeOf((*MockVdpaDevice)(nil).Driver)) +} + +// MgmtDev mocks base method. +func (m *MockVdpaDevice) MgmtDev() kvdpa.MgmtDev { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MgmtDev") + ret0, _ := ret[0].(kvdpa.MgmtDev) + return ret0 +} + +// MgmtDev indicates an expected call of MgmtDev. +func (mr *MockVdpaDeviceMockRecorder) MgmtDev() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgmtDev", reflect.TypeOf((*MockVdpaDevice)(nil).MgmtDev)) +} + +// Name mocks base method. +func (m *MockVdpaDevice) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockVdpaDeviceMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockVdpaDevice)(nil).Name)) +} + +// ParentDevicePath mocks base method. +func (m *MockVdpaDevice) ParentDevicePath() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParentDevicePath") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParentDevicePath indicates an expected call of ParentDevicePath. +func (mr *MockVdpaDeviceMockRecorder) ParentDevicePath() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParentDevicePath", reflect.TypeOf((*MockVdpaDevice)(nil).ParentDevicePath)) +} + +// VhostVdpa mocks base method. +func (m *MockVdpaDevice) VhostVdpa() kvdpa.VhostVdpa { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VhostVdpa") + ret0, _ := ret[0].(kvdpa.VhostVdpa) + return ret0 +} + +// VhostVdpa indicates an expected call of VhostVdpa. +func (mr *MockVdpaDeviceMockRecorder) VhostVdpa() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VhostVdpa", reflect.TypeOf((*MockVdpaDevice)(nil).VhostVdpa)) +} + +// VirtioNet mocks base method. +func (m *MockVdpaDevice) VirtioNet() kvdpa.VirtioNet { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VirtioNet") + ret0, _ := ret[0].(kvdpa.VirtioNet) + return ret0 +} + +// VirtioNet indicates an expected call of VirtioNet. +func (mr *MockVdpaDeviceMockRecorder) VirtioNet() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VirtioNet", reflect.TypeOf((*MockVdpaDevice)(nil).VirtioNet)) +} + +// MockGoVdpaLib is a mock of GoVdpaLib interface. +type MockGoVdpaLib struct { + ctrl *gomock.Controller + recorder *MockGoVdpaLibMockRecorder +} + +// MockGoVdpaLibMockRecorder is the mock recorder for MockGoVdpaLib. +type MockGoVdpaLibMockRecorder struct { + mock *MockGoVdpaLib +} + +// NewMockGoVdpaLib creates a new mock instance. +func NewMockGoVdpaLib(ctrl *gomock.Controller) *MockGoVdpaLib { + mock := &MockGoVdpaLib{ctrl: ctrl} + mock.recorder = &MockGoVdpaLibMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGoVdpaLib) EXPECT() *MockGoVdpaLibMockRecorder { + return m.recorder +} + +// AddVdpaDevice mocks base method. +func (m *MockGoVdpaLib) AddVdpaDevice(mgmtDeviceName, vdpaDeviceName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddVdpaDevice", mgmtDeviceName, vdpaDeviceName) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddVdpaDevice indicates an expected call of AddVdpaDevice. +func (mr *MockGoVdpaLibMockRecorder) AddVdpaDevice(mgmtDeviceName, vdpaDeviceName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVdpaDevice", reflect.TypeOf((*MockGoVdpaLib)(nil).AddVdpaDevice), mgmtDeviceName, vdpaDeviceName) +} + +// DeleteVdpaDevice mocks base method. +func (m *MockGoVdpaLib) DeleteVdpaDevice(vdpaDeviceName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteVdpaDevice", vdpaDeviceName) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteVdpaDevice indicates an expected call of DeleteVdpaDevice. +func (mr *MockGoVdpaLibMockRecorder) DeleteVdpaDevice(vdpaDeviceName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVdpaDevice", reflect.TypeOf((*MockGoVdpaLib)(nil).DeleteVdpaDevice), vdpaDeviceName) +} + +// GetVdpaDevice mocks base method. +func (m *MockGoVdpaLib) GetVdpaDevice(vdpaDeviceName string) (govdpa.VdpaDevice, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVdpaDevice", vdpaDeviceName) + ret0, _ := ret[0].(govdpa.VdpaDevice) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVdpaDevice indicates an expected call of GetVdpaDevice. +func (mr *MockGoVdpaLibMockRecorder) GetVdpaDevice(vdpaDeviceName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVdpaDevice", reflect.TypeOf((*MockGoVdpaLib)(nil).GetVdpaDevice), vdpaDeviceName) +} diff --git a/pkg/host/internal/lib/netlink/mock/mock_netlink.go b/pkg/host/internal/lib/netlink/mock/mock_netlink.go new file mode 100644 index 000000000..072ba8980 --- /dev/null +++ b/pkg/host/internal/lib/netlink/mock/mock_netlink.go @@ -0,0 +1,188 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: netlink.go + +// Package mock_netlink is a generated GoMock package. +package mock_netlink + +import ( + net "net" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + netlink "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink" + netlink0 "github.com/vishvananda/netlink" +) + +// MockLink is a mock of Link interface. +type MockLink struct { + ctrl *gomock.Controller + recorder *MockLinkMockRecorder +} + +// MockLinkMockRecorder is the mock recorder for MockLink. +type MockLinkMockRecorder struct { + mock *MockLink +} + +// NewMockLink creates a new mock instance. +func NewMockLink(ctrl *gomock.Controller) *MockLink { + mock := &MockLink{ctrl: ctrl} + mock.recorder = &MockLinkMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLink) EXPECT() *MockLinkMockRecorder { + return m.recorder +} + +// Attrs mocks base method. +func (m *MockLink) Attrs() *netlink0.LinkAttrs { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Attrs") + ret0, _ := ret[0].(*netlink0.LinkAttrs) + return ret0 +} + +// Attrs indicates an expected call of Attrs. +func (mr *MockLinkMockRecorder) Attrs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attrs", reflect.TypeOf((*MockLink)(nil).Attrs)) +} + +// Type mocks base method. +func (m *MockLink) Type() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Type") + ret0, _ := ret[0].(string) + return ret0 +} + +// Type indicates an expected call of Type. +func (mr *MockLinkMockRecorder) Type() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*MockLink)(nil).Type)) +} + +// MockNetlinkLib is a mock of NetlinkLib interface. +type MockNetlinkLib struct { + ctrl *gomock.Controller + recorder *MockNetlinkLibMockRecorder +} + +// MockNetlinkLibMockRecorder is the mock recorder for MockNetlinkLib. +type MockNetlinkLibMockRecorder struct { + mock *MockNetlinkLib +} + +// NewMockNetlinkLib creates a new mock instance. +func NewMockNetlinkLib(ctrl *gomock.Controller) *MockNetlinkLib { + mock := &MockNetlinkLib{ctrl: ctrl} + mock.recorder = &MockNetlinkLibMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNetlinkLib) EXPECT() *MockNetlinkLibMockRecorder { + return m.recorder +} + +// DevLinkGetDeviceByName mocks base method. +func (m *MockNetlinkLib) DevLinkGetDeviceByName(bus, device string) (*netlink0.DevlinkDevice, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DevLinkGetDeviceByName", bus, device) + ret0, _ := ret[0].(*netlink0.DevlinkDevice) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DevLinkGetDeviceByName indicates an expected call of DevLinkGetDeviceByName. +func (mr *MockNetlinkLibMockRecorder) DevLinkGetDeviceByName(bus, device interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DevLinkGetDeviceByName", reflect.TypeOf((*MockNetlinkLib)(nil).DevLinkGetDeviceByName), bus, device) +} + +// DevLinkSetEswitchMode mocks base method. +func (m *MockNetlinkLib) DevLinkSetEswitchMode(dev *netlink0.DevlinkDevice, newMode string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DevLinkSetEswitchMode", dev, newMode) + ret0, _ := ret[0].(error) + return ret0 +} + +// DevLinkSetEswitchMode indicates an expected call of DevLinkSetEswitchMode. +func (mr *MockNetlinkLibMockRecorder) DevLinkSetEswitchMode(dev, newMode interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DevLinkSetEswitchMode", reflect.TypeOf((*MockNetlinkLib)(nil).DevLinkSetEswitchMode), dev, newMode) +} + +// LinkByName mocks base method. +func (m *MockNetlinkLib) LinkByName(name string) (netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkByName", name) + ret0, _ := ret[0].(netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LinkByName indicates an expected call of LinkByName. +func (mr *MockNetlinkLibMockRecorder) LinkByName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetlinkLib)(nil).LinkByName), name) +} + +// LinkSetUp mocks base method. +func (m *MockNetlinkLib) LinkSetUp(link netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkSetUp", link) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkSetUp indicates an expected call of LinkSetUp. +func (mr *MockNetlinkLibMockRecorder) LinkSetUp(link interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetlinkLib)(nil).LinkSetUp), link) +} + +// LinkSetVfHardwareAddr mocks base method. +func (m *MockNetlinkLib) LinkSetVfHardwareAddr(link netlink.Link, vf int, hwaddr net.HardwareAddr) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkSetVfHardwareAddr", link, vf, hwaddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkSetVfHardwareAddr indicates an expected call of LinkSetVfHardwareAddr. +func (mr *MockNetlinkLibMockRecorder) LinkSetVfHardwareAddr(link, vf, hwaddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetVfHardwareAddr", reflect.TypeOf((*MockNetlinkLib)(nil).LinkSetVfHardwareAddr), link, vf, hwaddr) +} + +// LinkSetVfNodeGUID mocks base method. +func (m *MockNetlinkLib) LinkSetVfNodeGUID(link netlink.Link, vf int, nodeguid net.HardwareAddr) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkSetVfNodeGUID", link, vf, nodeguid) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkSetVfNodeGUID indicates an expected call of LinkSetVfNodeGUID. +func (mr *MockNetlinkLibMockRecorder) LinkSetVfNodeGUID(link, vf, nodeguid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetVfNodeGUID", reflect.TypeOf((*MockNetlinkLib)(nil).LinkSetVfNodeGUID), link, vf, nodeguid) +} + +// LinkSetVfPortGUID mocks base method. +func (m *MockNetlinkLib) LinkSetVfPortGUID(link netlink.Link, vf int, portguid net.HardwareAddr) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkSetVfPortGUID", link, vf, portguid) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkSetVfPortGUID indicates an expected call of LinkSetVfPortGUID. +func (mr *MockNetlinkLibMockRecorder) LinkSetVfPortGUID(link, vf, portguid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetVfPortGUID", reflect.TypeOf((*MockNetlinkLib)(nil).LinkSetVfPortGUID), link, vf, portguid) +} diff --git a/pkg/host/internal/lib/netlink/netlink.go b/pkg/host/internal/lib/netlink/netlink.go new file mode 100644 index 000000000..4871abe95 --- /dev/null +++ b/pkg/host/internal/lib/netlink/netlink.go @@ -0,0 +1,86 @@ +package netlink + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +func New() NetlinkLib { + return &libWrapper{} +} + +type Link interface { + netlink.Link +} + +//go:generate ../../../../../bin/mockgen -destination mock/mock_netlink.go -source netlink.go +type NetlinkLib interface { + // LinkSetVfNodeGUID sets the node GUID of a vf for the link. + // Equivalent to: `ip link set dev $link vf $vf node_guid $nodeguid` + LinkSetVfNodeGUID(link Link, vf int, nodeguid net.HardwareAddr) error + // LinkSetVfPortGUID sets the port GUID of a vf for the link. + // Equivalent to: `ip link set dev $link vf $vf port_guid $portguid` + LinkSetVfPortGUID(link Link, vf int, portguid net.HardwareAddr) error + // LinkByName finds a link by name and returns a pointer to the object. + LinkByName(name string) (Link, error) + // LinkSetVfHardwareAddr sets the hardware address of a vf for the link. + // Equivalent to: `ip link set $link vf $vf mac $hwaddr` + LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error + // LinkSetUp enables the link device. + // Equivalent to: `ip link set $link up` + LinkSetUp(link Link) error + // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, + // otherwise returns an error code. + DevLinkGetDeviceByName(bus string, device string) (*netlink.DevlinkDevice, error) + // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or + // returns an error code. + // Equivalent to: `devlink dev eswitch set $dev mode switchdev` + // Equivalent to: `devlink dev eswitch set $dev mode legacy` + DevLinkSetEswitchMode(dev *netlink.DevlinkDevice, newMode string) error +} + +type libWrapper struct{} + +// LinkSetVfNodeGUID sets the node GUID of a vf for the link. +// Equivalent to: `ip link set dev $link vf $vf node_guid $nodeguid` +func (w *libWrapper) LinkSetVfNodeGUID(link Link, vf int, nodeguid net.HardwareAddr) error { + return netlink.LinkSetVfNodeGUID(link, vf, nodeguid) +} + +// LinkSetVfPortGUID sets the port GUID of a vf for the link. +// Equivalent to: `ip link set dev $link vf $vf port_guid $portguid` +func (w *libWrapper) LinkSetVfPortGUID(link Link, vf int, portguid net.HardwareAddr) error { + return netlink.LinkSetVfPortGUID(link, vf, portguid) +} + +// LinkByName finds a link by name and returns a pointer to the object.// LinkByName finds a link by name and returns a pointer to the object. +func (w *libWrapper) LinkByName(name string) (Link, error) { + return netlink.LinkByName(name) +} + +// LinkSetVfHardwareAddr sets the hardware address of a vf for the link. +// Equivalent to: `ip link set $link vf $vf mac $hwaddr` +func (w *libWrapper) LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { + return netlink.LinkSetVfHardwareAddr(link, vf, hwaddr) +} + +// LinkSetUp enables the link device. +// Equivalent to: `ip link set $link up` +func (w *libWrapper) LinkSetUp(link Link) error { + return netlink.LinkSetUp(link) +} + +// DevlinkGetDeviceByName provides a pointer to devlink device and nil error, +// otherwise returns an error code. +func (w *libWrapper) DevLinkGetDeviceByName(bus string, device string) (*netlink.DevlinkDevice, error) { + return netlink.DevLinkGetDeviceByName(bus, device) +} + +// DevLinkSetEswitchMode sets eswitch mode if able to set successfully or +// returns an error code. +// Equivalent to: `devlink dev eswitch set $dev mode switchdev` +// Equivalent to: `devlink dev eswitch set $dev mode legacy` +func (w *libWrapper) DevLinkSetEswitchMode(dev *netlink.DevlinkDevice, newMode string) error { + return netlink.DevLinkSetEswitchMode(dev, newMode) +} diff --git a/pkg/host/internal/network/network.go b/pkg/host/internal/network/network.go new file mode 100644 index 000000000..f9ede5fc8 --- /dev/null +++ b/pkg/host/internal/network/network.go @@ -0,0 +1,198 @@ +package network + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/cenkalti/backoff" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" +) + +type network struct { + utilsHelper utils.CmdInterface + dputilsLib dputilsPkg.DPUtilsLib +} + +func New(utilsHelper utils.CmdInterface, dputilsLib dputilsPkg.DPUtilsLib) types.NetworkInterface { + return &network{ + utilsHelper: utilsHelper, + dputilsLib: dputilsLib, + } +} + +// TryToGetVirtualInterfaceName get the interface name of a virtio interface +func (n *network) TryToGetVirtualInterfaceName(pciAddr string) string { + log.Log.Info("TryToGetVirtualInterfaceName() get interface name for device", "device", pciAddr) + + // To support different driver that is not virtio-pci like mlx + name := n.TryGetInterfaceName(pciAddr) + if name != "" { + return name + } + + netDir, err := filepath.Glob(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "virtio*", "net")) + if err != nil || len(netDir) < 1 { + return "" + } + + fInfos, err := os.ReadDir(netDir[0]) + if err != nil { + log.Log.Error(err, "TryToGetVirtualInterfaceName(): failed to read net directory", "dir", netDir[0]) + return "" + } + + names := make([]string, 0) + for _, f := range fInfos { + names = append(names, f.Name()) + } + + if len(names) < 1 { + return "" + } + + return names[0] +} + +func (n *network) TryGetInterfaceName(pciAddr string) string { + names, err := n.dputilsLib.GetNetNames(pciAddr) + if err != nil || len(names) < 1 { + return "" + } + netDevName := names[0] + + // Switchdev PF and their VFs representors are existing under the same PCI address since kernel 5.8 + // if device is switchdev then return PF name + for _, name := range names { + if !n.IsSwitchdev(name) { + continue + } + // Try to get the phys port name, if not exists then fallback to check without it + // phys_port_name should be in formant p e.g p0,p1,p2 ...etc. + if physPortName, err := n.GetPhysPortName(name); err == nil { + if !vars.PfPhysPortNameRe.MatchString(physPortName) { + continue + } + } + return name + } + + log.Log.V(2).Info("tryGetInterfaceName()", "name", netDevName) + return netDevName +} + +func (n *network) GetPhysSwitchID(name string) (string, error) { + swIDFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_switch_id") + physSwitchID, err := os.ReadFile(swIDFile) + if err != nil { + return "", err + } + if physSwitchID != nil { + return strings.TrimSpace(string(physSwitchID)), nil + } + return "", nil +} + +func (n *network) GetPhysPortName(name string) (string, error) { + devicePortNameFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_port_name") + physPortName, err := os.ReadFile(devicePortNameFile) + if err != nil { + return "", err + } + if physPortName != nil { + return strings.TrimSpace(string(physPortName)), nil + } + return "", nil +} + +func (n *network) IsSwitchdev(name string) bool { + switchID, err := n.GetPhysSwitchID(name) + if err != nil || switchID == "" { + return false + } + + return true +} + +func mtuFilePath(ifaceName string, pciAddr string) string { + mtuFile := "net/" + ifaceName + "/mtu" + return filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, mtuFile) +} + +func (n *network) GetNetdevMTU(pciAddr string) int { + log.Log.V(2).Info("GetNetdevMTU(): get MTU", "device", pciAddr) + ifaceName := n.TryGetInterfaceName(pciAddr) + if ifaceName == "" { + return 0 + } + data, err := os.ReadFile(mtuFilePath(ifaceName, pciAddr)) + if err != nil { + log.Log.Error(err, "GetNetdevMTU(): fail to read mtu file", "path", mtuFilePath) + return 0 + } + mtu, err := strconv.Atoi(strings.TrimSpace(string(data))) + if err != nil { + log.Log.Error(err, "GetNetdevMTU(): fail to convert mtu to int", "raw-mtu", strings.TrimSpace(string(data))) + return 0 + } + return mtu +} + +func (n *network) SetNetdevMTU(pciAddr string, mtu int) error { + log.Log.V(2).Info("SetNetdevMTU(): set MTU", "device", pciAddr, "mtu", mtu) + if mtu <= 0 { + log.Log.V(2).Info("SetNetdevMTU(): refusing to set MTU", "mtu", mtu) + return nil + } + b := backoff.NewConstantBackOff(1 * time.Second) + err := backoff.Retry(func() error { + ifaceName, err := n.dputilsLib.GetNetNames(pciAddr) + if err != nil { + log.Log.Error(err, "SetNetdevMTU(): fail to get interface name", "device", pciAddr) + return err + } + if len(ifaceName) < 1 { + return fmt.Errorf("SetNetdevMTU(): interface name is empty") + } + mtuFilePath := mtuFilePath(ifaceName[0], pciAddr) + return os.WriteFile(mtuFilePath, []byte(strconv.Itoa(mtu)), os.ModeAppend) + }, backoff.WithMaxRetries(b, 10)) + if err != nil { + log.Log.Error(err, "SetNetdevMTU(): fail to write mtu file after retrying") + return err + } + return nil +} + +func (n *network) GetNetDevMac(ifaceName string) string { + log.Log.V(2).Info("GetNetDevMac(): get Mac", "device", ifaceName) + macFilePath := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, ifaceName, "address") + data, err := os.ReadFile(macFilePath) + if err != nil { + log.Log.Error(err, "GetNetDevMac(): fail to read Mac file", "path", macFilePath) + return "" + } + + return strings.TrimSpace(string(data)) +} + +func (n *network) GetNetDevLinkSpeed(ifaceName string) string { + log.Log.V(2).Info("GetNetDevLinkSpeed(): get LinkSpeed", "device", ifaceName) + speedFilePath := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, ifaceName, "speed") + data, err := os.ReadFile(speedFilePath) + if err != nil { + log.Log.Error(err, "GetNetDevLinkSpeed(): fail to read Link Speed file", "path", speedFilePath) + return "" + } + + return fmt.Sprintf("%s Mb/s", strings.TrimSpace(string(data))) +} diff --git a/pkg/host/internal/service/service.go b/pkg/host/internal/service/service.go new file mode 100644 index 000000000..4ecec5b5f --- /dev/null +++ b/pkg/host/internal/service/service.go @@ -0,0 +1,262 @@ +package service + +import ( + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + + "github.com/coreos/go-systemd/v22/unit" + "gopkg.in/yaml.v3" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" +) + +// TODO: handle this to support unit-tests +const systemdDir = "/usr/lib/systemd/system/" + +type service struct { + utilsHelper utils.CmdInterface +} + +func New(utilsHelper utils.CmdInterface) types.ServiceInterface { + return &service{utilsHelper: utilsHelper} +} + +// ServiceInjectionManifestFile service injection manifest file structure +type ServiceInjectionManifestFile struct { + Name string + Dropins []struct { + Contents string + } +} + +// IsServiceExist check if service unit exist +func (s *service) IsServiceExist(servicePath string) (bool, error) { + _, err := os.Stat(path.Join(consts.Chroot, servicePath)) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + + return true, nil +} + +// IsServiceEnabled check if service exist and enabled +func (s *service) IsServiceEnabled(servicePath string) (bool, error) { + exist, err := s.IsServiceExist(servicePath) + if err != nil || !exist { + return false, err + } + serviceName := filepath.Base(servicePath) + // Change root dir + exit, err := s.utilsHelper.Chroot(consts.Chroot) + if err != nil { + return false, err + } + defer exit() + + // TODO: add check for the output and logs + _, _, err = s.utilsHelper.RunCommand("systemctl", "is-enabled", serviceName) + return err == nil, nil +} + +// ReadService read service from given path +func (s *service) ReadService(servicePath string) (*types.Service, error) { + data, err := os.ReadFile(path.Join(consts.Chroot, servicePath)) + if err != nil { + return nil, err + } + + return &types.Service{ + Name: filepath.Base(servicePath), + Path: servicePath, + Content: string(data), + }, nil +} + +// EnableService creates service file and enables it with systemctl enable +func (s *service) EnableService(service *types.Service) error { + // Write service file + err := os.WriteFile(path.Join(consts.Chroot, service.Path), []byte(service.Content), 0644) + if err != nil { + return err + } + + // Change root dir + exit, err := s.utilsHelper.Chroot(consts.Chroot) + if err != nil { + return err + } + defer exit() + + // Enable service + _, _, err = s.utilsHelper.RunCommand("systemctl", "enable", service.Name) + return err +} + +// CompareServices compare 2 service and return true if serviceA has all the fields of serviceB +func (s *service) CompareServices(serviceA, serviceB *types.Service) (bool, error) { + optsA, err := unit.Deserialize(strings.NewReader(serviceA.Content)) + if err != nil { + return false, err + } + optsB, err := unit.Deserialize(strings.NewReader(serviceB.Content)) + if err != nil { + return false, err + } + +OUTER: + for _, optB := range optsB { + for _, optA := range optsA { + if optA.Match(optB) { + continue OUTER + } + } + log.Log.V(2).Info("CompareServices", "ServiceA", optsA, "ServiceB", *optB) + return true, nil + } + + return false, nil +} + +// RemoveFromService removes given fields from service +func (s *service) RemoveFromService(service *types.Service, options ...*unit.UnitOption) (*types.Service, error) { + opts, err := unit.Deserialize(strings.NewReader(service.Content)) + if err != nil { + return nil, err + } + + var newServiceOptions []*unit.UnitOption +OUTER: + for _, opt := range opts { + for _, optRemove := range options { + if opt.Match(optRemove) { + continue OUTER + } + } + + newServiceOptions = append(newServiceOptions, opt) + } + + data, err := io.ReadAll(unit.Serialize(newServiceOptions)) + if err != nil { + return nil, err + } + + return &types.Service{ + Name: service.Name, + Path: service.Path, + Content: string(data), + }, nil +} + +// ReadServiceInjectionManifestFile reads service injection file +func (s *service) ReadServiceInjectionManifestFile(path string) (*types.Service, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var serviceContent ServiceInjectionManifestFile + if err := yaml.Unmarshal(data, &serviceContent); err != nil { + return nil, err + } + + return &types.Service{ + Name: serviceContent.Name, + Path: systemdDir + serviceContent.Name, + Content: serviceContent.Dropins[0].Contents, + }, nil +} + +// ReadServiceManifestFile reads service file +func (s *service) ReadServiceManifestFile(path string) (*types.Service, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var serviceFile *types.ServiceManifestFile + if err := yaml.Unmarshal(data, &serviceFile); err != nil { + return nil, err + } + + return &types.Service{ + Name: serviceFile.Name, + Path: "/etc/systemd/system/" + serviceFile.Name, + Content: serviceFile.Contents, + }, nil +} + +// ReadScriptManifestFile reads script file +func (s *service) ReadScriptManifestFile(path string) (*types.ScriptManifestFile, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var scriptFile *types.ScriptManifestFile + if err := yaml.Unmarshal(data, &scriptFile); err != nil { + return nil, err + } + + return scriptFile, nil +} + +func (s *service) UpdateSystemService(serviceObj *types.Service) error { + systemService, err := s.ReadService(serviceObj.Path) + if err != nil { + return err + } + if systemService == nil { + // Invalid case to reach here + return fmt.Errorf("can't update non-existing service %q", serviceObj.Name) + } + serviceOptions, err := unit.Deserialize(strings.NewReader(serviceObj.Content)) + if err != nil { + return err + } + updatedService, err := appendToService(systemService, serviceOptions...) + if err != nil { + return err + } + + return s.EnableService(updatedService) +} + +// appendToService appends given fields to service +func appendToService(service *types.Service, options ...*unit.UnitOption) (*types.Service, error) { + serviceOptions, err := unit.Deserialize(strings.NewReader(service.Content)) + if err != nil { + return nil, err + } + +OUTER: + for _, appendOpt := range options { + for _, opt := range serviceOptions { + if opt.Match(appendOpt) { + continue OUTER + } + } + serviceOptions = append(serviceOptions, appendOpt) + } + + data, err := io.ReadAll(unit.Serialize(serviceOptions)) + if err != nil { + return nil, err + } + + return &types.Service{ + Name: service.Name, + Path: service.Path, + Content: string(data), + }, nil +} diff --git a/pkg/host/internal/sriov/sriov.go b/pkg/host/internal/sriov/sriov.go new file mode 100644 index 000000000..3d8d0a48c --- /dev/null +++ b/pkg/host/internal/sriov/sriov.go @@ -0,0 +1,631 @@ +package sriov + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "github.com/jaypipes/ghw" + "github.com/vishvananda/netlink" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/log" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils" + netlinkPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + mlx "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" +) + +type sriov struct { + utilsHelper utils.CmdInterface + kernelHelper types.KernelInterface + networkHelper types.NetworkInterface + udevHelper types.UdevInterface + netlinkLib netlinkPkg.NetlinkLib + dputilsLib dputilsPkg.DPUtilsLib +} + +func New(utilsHelper utils.CmdInterface, + kernelHelper types.KernelInterface, + networkHelper types.NetworkInterface, + udevHelper types.UdevInterface, + netlinkLib netlinkPkg.NetlinkLib, + dputilsLib dputilsPkg.DPUtilsLib) types.SriovInterface { + return &sriov{utilsHelper: utilsHelper, + kernelHelper: kernelHelper, + networkHelper: networkHelper, + udevHelper: udevHelper, + netlinkLib: netlinkLib, + dputilsLib: dputilsLib, + } +} + +func (s *sriov) SetSriovNumVfs(pciAddr string, numVfs int) error { + log.Log.V(2).Info("SetSriovNumVfs(): set NumVfs", "device", pciAddr, "numVfs", numVfs) + numVfsFilePath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, consts.NumVfsFile) + bs := []byte(strconv.Itoa(numVfs)) + err := os.WriteFile(numVfsFilePath, []byte("0"), os.ModeAppend) + if err != nil { + log.Log.Error(err, "SetSriovNumVfs(): fail to reset NumVfs file", "path", numVfsFilePath) + return err + } + if numVfs == 0 { + return nil + } + err = os.WriteFile(numVfsFilePath, bs, os.ModeAppend) + if err != nil { + log.Log.Error(err, "SetSriovNumVfs(): fail to set NumVfs file", "path", numVfsFilePath) + return err + } + return nil +} + +func (s *sriov) ResetSriovDevice(ifaceStatus sriovnetworkv1.InterfaceExt) error { + log.Log.V(2).Info("ResetSriovDevice(): reset SRIOV device", "address", ifaceStatus.PciAddress) + if err := s.SetSriovNumVfs(ifaceStatus.PciAddress, 0); err != nil { + return err + } + if ifaceStatus.LinkType == consts.LinkTypeETH { + var mtu int + is := sriovnetworkv1.InitialState.GetInterfaceStateByPciAddress(ifaceStatus.PciAddress) + if is != nil { + mtu = is.Mtu + } else { + mtu = 1500 + } + log.Log.V(2).Info("ResetSriovDevice(): reset mtu", "value", mtu) + if err := s.networkHelper.SetNetdevMTU(ifaceStatus.PciAddress, mtu); err != nil { + return err + } + } else if ifaceStatus.LinkType == consts.LinkTypeIB { + if err := s.networkHelper.SetNetdevMTU(ifaceStatus.PciAddress, 2048); err != nil { + return err + } + } + return nil +} + +func (s *sriov) GetVfInfo(pciAddr string, devices []*ghw.PCIDevice) sriovnetworkv1.VirtualFunction { + driver, err := s.dputilsLib.GetDriverName(pciAddr) + if err != nil { + log.Log.Error(err, "getVfInfo(): unable to parse device driver", "device", pciAddr) + } + id, err := s.dputilsLib.GetVFID(pciAddr) + if err != nil { + log.Log.Error(err, "getVfInfo(): unable to get VF index", "device", pciAddr) + } + vf := sriovnetworkv1.VirtualFunction{ + PciAddress: pciAddr, + Driver: driver, + VfID: id, + } + + if mtu := s.networkHelper.GetNetdevMTU(pciAddr); mtu > 0 { + vf.Mtu = mtu + } + if name := s.networkHelper.TryGetInterfaceName(pciAddr); name != "" { + vf.Name = name + vf.Mac = s.networkHelper.GetNetDevMac(name) + } + + for _, device := range devices { + if pciAddr == device.Address { + vf.Vendor = device.Vendor.ID + vf.DeviceID = device.Product.ID + break + } + continue + } + return vf +} + +func (s *sriov) SetVfGUID(vfAddr string, pfLink netlink.Link) error { + log.Log.Info("SetVfGUID()", "vf", vfAddr) + vfID, err := s.dputilsLib.GetVFID(vfAddr) + if err != nil { + log.Log.Error(err, "SetVfGUID(): unable to get VF id", "address", vfAddr) + return err + } + guid := utils.GenerateRandomGUID() + if err := s.netlinkLib.LinkSetVfNodeGUID(pfLink, vfID, guid); err != nil { + return err + } + if err := s.netlinkLib.LinkSetVfPortGUID(pfLink, vfID, guid); err != nil { + return err + } + if err = s.kernelHelper.Unbind(vfAddr); err != nil { + return err + } + + return nil +} + +func (s *sriov) VFIsReady(pciAddr string) (netlink.Link, error) { + log.Log.Info("VFIsReady()", "device", pciAddr) + var err error + var vfLink netlink.Link + err = wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { + vfName := s.networkHelper.TryGetInterfaceName(pciAddr) + vfLink, err = s.netlinkLib.LinkByName(vfName) + if err != nil { + log.Log.Error(err, "VFIsReady(): unable to get VF link", "device", pciAddr) + } + return err == nil, nil + }) + if err != nil { + return vfLink, err + } + return vfLink, nil +} + +func (s *sriov) SetVfAdminMac(vfAddr string, pfLink, vfLink netlink.Link) error { + log.Log.Info("SetVfAdminMac()", "vf", vfAddr) + + vfID, err := s.dputilsLib.GetVFID(vfAddr) + if err != nil { + log.Log.Error(err, "SetVfAdminMac(): unable to get VF id", "address", vfAddr) + return err + } + + if err := s.netlinkLib.LinkSetVfHardwareAddr(pfLink, vfID, vfLink.Attrs().HardwareAddr); err != nil { + return err + } + + return nil +} + +func (s *sriov) DiscoverSriovDevices(storeManager store.ManagerInterface) ([]sriovnetworkv1.InterfaceExt, error) { + log.Log.V(2).Info("DiscoverSriovDevices") + pfList := []sriovnetworkv1.InterfaceExt{} + + pci, err := ghw.PCI() + if err != nil { + return nil, fmt.Errorf("DiscoverSriovDevices(): error getting PCI info: %v", err) + } + + devices := pci.ListDevices() + if len(devices) == 0 { + return nil, fmt.Errorf("DiscoverSriovDevices(): could not retrieve PCI devices") + } + + for _, device := range devices { + devClass, err := strconv.ParseInt(device.Class.ID, 16, 64) + if err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device class, skipping", + "device", device) + continue + } + if devClass != consts.NetClass { + // Not network device + continue + } + + // TODO: exclude devices used by host system + + if s.dputilsLib.IsSriovVF(device.Address) { + continue + } + + driver, err := s.dputilsLib.GetDriverName(device.Address) + if err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device driver for device, skipping", "device", device) + continue + } + + deviceNames, err := s.dputilsLib.GetNetNames(device.Address) + if err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): unable to get device names for device, skipping", "device", device) + continue + } + + if len(deviceNames) == 0 { + // no network devices found, skipping device + continue + } + + if !vars.DevMode { + if !sriovnetworkv1.IsSupportedModel(device.Vendor.ID, device.Product.ID) { + log.Log.Info("DiscoverSriovDevices(): unsupported device", "device", device) + continue + } + } + + iface := sriovnetworkv1.InterfaceExt{ + PciAddress: device.Address, + Driver: driver, + Vendor: device.Vendor.ID, + DeviceID: device.Product.ID, + } + if mtu := s.networkHelper.GetNetdevMTU(device.Address); mtu > 0 { + iface.Mtu = mtu + } + if name := s.networkHelper.TryGetInterfaceName(device.Address); name != "" { + iface.Name = name + iface.Mac = s.networkHelper.GetNetDevMac(name) + iface.LinkSpeed = s.networkHelper.GetNetDevLinkSpeed(name) + } + iface.LinkType = s.GetLinkType(iface) + + pfStatus, exist, err := storeManager.LoadPfsStatus(iface.PciAddress) + if err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): failed to load PF status from disk") + } else { + if exist { + iface.ExternallyManaged = pfStatus.ExternallyManaged + } + } + + if s.dputilsLib.IsSriovPF(device.Address) { + iface.TotalVfs = s.dputilsLib.GetSriovVFcapacity(device.Address) + iface.NumVfs = s.dputilsLib.GetVFconfigured(device.Address) + if iface.EswitchMode, err = s.GetNicSriovMode(device.Address); err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): warning, unable to get device eswitch mode", + "device", device.Address) + } + if s.dputilsLib.SriovConfigured(device.Address) { + vfs, err := s.dputilsLib.GetVFList(device.Address) + if err != nil { + log.Log.Error(err, "DiscoverSriovDevices(): unable to parse VFs for device, skipping", + "device", device) + continue + } + for _, vf := range vfs { + instance := s.GetVfInfo(vf, devices) + iface.VFs = append(iface.VFs, instance) + } + } + } + pfList = append(pfList, iface) + } + + return pfList, nil +} + +func (s *sriov) ConfigSriovDevice(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) error { + log.Log.V(2).Info("configSriovDevice(): configure sriov device", + "device", iface.PciAddress, "config", iface) + var err error + if iface.NumVfs > ifaceStatus.TotalVfs { + err := fmt.Errorf("cannot config SRIOV device: NumVfs (%d) is larger than TotalVfs (%d)", iface.NumVfs, ifaceStatus.TotalVfs) + log.Log.Error(err, "configSriovDevice(): fail to set NumVfs for device", "device", iface.PciAddress) + return err + } + // set numVFs + if iface.NumVfs != ifaceStatus.NumVfs { + if iface.ExternallyManaged { + if iface.NumVfs > ifaceStatus.NumVfs { + errMsg := fmt.Sprintf("configSriovDevice(): number of request virtual functions %d is not equal to configured virtual functions %d but the policy is configured as ExternallyManaged for device %s", iface.NumVfs, ifaceStatus.NumVfs, iface.PciAddress) + log.Log.Error(nil, errMsg) + return fmt.Errorf(errMsg) + } + } else { + // create the udev rule to disable all the vfs from network manager as this vfs are managed by the operator + err = s.udevHelper.AddUdevRule(iface.PciAddress) + if err != nil { + return err + } + + err = s.SetSriovNumVfs(iface.PciAddress, iface.NumVfs) + if err != nil { + log.Log.Error(err, "configSriovDevice(): fail to set NumVfs for device", "device", iface.PciAddress) + errRemove := s.udevHelper.RemoveUdevRule(iface.PciAddress) + if errRemove != nil { + log.Log.Error(errRemove, "configSriovDevice(): fail to remove udev rule", "device", iface.PciAddress) + } + return err + } + } + } + // set PF mtu + if iface.Mtu > 0 && iface.Mtu > ifaceStatus.Mtu { + if iface.ExternallyManaged { + err := fmt.Errorf("ConfigSriovDevice(): requested MTU(%d) is greater than configured MTU(%d) for device %s. cannot change MTU as policy is configured as ExternallyManaged", + iface.Mtu, ifaceStatus.Mtu, iface.PciAddress) + log.Log.Error(nil, err.Error()) + return err + } + err = s.networkHelper.SetNetdevMTU(iface.PciAddress, iface.Mtu) + if err != nil { + log.Log.Error(err, "configSriovDevice(): fail to set mtu for PF", "device", iface.PciAddress) + return err + } + } + // Config VFs + if iface.NumVfs > 0 { + vfAddrs, err := s.dputilsLib.GetVFList(iface.PciAddress) + if err != nil { + log.Log.Error(err, "configSriovDevice(): unable to parse VFs for device", "device", iface.PciAddress) + } + pfLink, err := s.netlinkLib.LinkByName(iface.Name) + if err != nil { + log.Log.Error(err, "configSriovDevice(): unable to get PF link for device", "device", iface) + return err + } + + for _, addr := range vfAddrs { + var group *sriovnetworkv1.VfGroup + + vfID, err := s.dputilsLib.GetVFID(addr) + if err != nil { + log.Log.Error(err, "configSriovDevice(): unable to get VF id", "device", iface.PciAddress) + return err + } + + for i := range iface.VfGroups { + if sriovnetworkv1.IndexInRange(vfID, iface.VfGroups[i].VfRange) { + group = &iface.VfGroups[i] + break + } + } + + // VF group not found. + if group == nil { + continue + } + + // only set GUID and MAC for VF with default driver + // for userspace drivers like vfio we configure the vf mac using the kernel nic mac address + // before we switch to the userspace driver + if yes, d := s.kernelHelper.HasDriver(addr); yes && !sriovnetworkv1.StringInArray(d, vars.DpdkDrivers) { + // LinkType is an optional field. Let's fallback to current link type + // if nothing is specified in the SriovNodePolicy + linkType := iface.LinkType + if linkType == "" { + linkType = ifaceStatus.LinkType + } + if strings.EqualFold(linkType, consts.LinkTypeIB) { + if err = s.SetVfGUID(addr, pfLink); err != nil { + return err + } + } else { + vfLink, err := s.VFIsReady(addr) + if err != nil { + log.Log.Error(err, "configSriovDevice(): VF link is not ready", "address", addr) + err = s.kernelHelper.RebindVfToDefaultDriver(addr) + if err != nil { + log.Log.Error(err, "configSriovDevice(): failed to rebind VF", "address", addr) + return err + } + + // Try to check the VF status again + vfLink, err = s.VFIsReady(addr) + if err != nil { + log.Log.Error(err, "configSriovDevice(): VF link is not ready", "address", addr) + return err + } + } + if err = s.SetVfAdminMac(addr, pfLink, vfLink); err != nil { + log.Log.Error(err, "configSriovDevice(): fail to configure VF admin mac", "device", addr) + return err + } + } + } + + if err = s.kernelHelper.UnbindDriverIfNeeded(addr, group.IsRdma); err != nil { + return err + } + + if !sriovnetworkv1.StringInArray(group.DeviceType, vars.DpdkDrivers) { + if err := s.kernelHelper.BindDefaultDriver(addr); err != nil { + log.Log.Error(err, "configSriovDevice(): fail to bind default driver for device", "device", addr) + return err + } + // only set MTU for VF with default driver + if group.Mtu > 0 { + if err := s.networkHelper.SetNetdevMTU(addr, group.Mtu); err != nil { + log.Log.Error(err, "configSriovDevice(): fail to set mtu for VF", "address", addr) + return err + } + } + } else { + if err := s.kernelHelper.BindDpdkDriver(addr, group.DeviceType); err != nil { + log.Log.Error(err, "configSriovDevice(): fail to bind driver for device", + "driver", group.DeviceType, "device", addr) + return err + } + } + } + } + // Set PF link up + pfLink, err := s.netlinkLib.LinkByName(ifaceStatus.Name) + if err != nil { + return err + } + if pfLink.Attrs().OperState != netlink.OperUp { + err = s.netlinkLib.LinkSetUp(pfLink) + if err != nil { + return err + } + } + return nil +} + +func (s *sriov) ConfigSriovInterfaces(storeManager store.ManagerInterface, + interfaces []sriovnetworkv1.Interface, ifaceStatuses []sriovnetworkv1.InterfaceExt, pfsToConfig map[string]bool) error { + if s.kernelHelper.IsKernelLockdownMode() && mlx.HasMellanoxInterfacesInSpec(ifaceStatuses, interfaces) { + log.Log.Error(nil, "cannot use mellanox devices when in kernel lockdown mode") + return fmt.Errorf("cannot use mellanox devices when in kernel lockdown mode") + } + + for _, ifaceStatus := range ifaceStatuses { + configured := false + for _, iface := range interfaces { + if iface.PciAddress == ifaceStatus.PciAddress { + configured = true + + if skip := pfsToConfig[iface.PciAddress]; skip { + break + } + + if !sriovnetworkv1.NeedToUpdateSriov(&iface, &ifaceStatus) { + log.Log.V(2).Info("syncNodeState(): no need update interface", "address", iface.PciAddress) + + // Save the PF status to the host + err := storeManager.SaveLastPfAppliedStatus(&iface) + if err != nil { + log.Log.Error(err, "SyncNodeState(): failed to save PF applied config to host") + return err + } + + break + } + if err := s.ConfigSriovDevice(&iface, &ifaceStatus); err != nil { + log.Log.Error(err, "SyncNodeState(): fail to configure sriov interface. resetting interface.", "address", iface.PciAddress) + if iface.ExternallyManaged { + log.Log.Info("SyncNodeState(): skipping device reset as the nic is marked as externally created") + } else { + if resetErr := s.ResetSriovDevice(ifaceStatus); resetErr != nil { + log.Log.Error(resetErr, "SyncNodeState(): failed to reset on error SR-IOV interface") + } + } + return err + } + + // Save the PF status to the host + err := storeManager.SaveLastPfAppliedStatus(&iface) + if err != nil { + log.Log.Error(err, "SyncNodeState(): failed to save PF applied config to host") + return err + } + break + } + } + if !configured && ifaceStatus.NumVfs > 0 { + if skip := pfsToConfig[ifaceStatus.PciAddress]; skip { + continue + } + + // load the PF info + pfStatus, exist, err := storeManager.LoadPfsStatus(ifaceStatus.PciAddress) + if err != nil { + log.Log.Error(err, "SyncNodeState(): failed to load info about PF status for device", + "address", ifaceStatus.PciAddress) + return err + } + + if !exist { + log.Log.Info("SyncNodeState(): PF name with pci address has VFs configured but they weren't created by the sriov operator. Skipping the device reset", + "pf-name", ifaceStatus.Name, + "address", ifaceStatus.PciAddress) + continue + } + + if pfStatus.ExternallyManaged { + log.Log.Info("SyncNodeState(): PF name with pci address was externally created skipping the device reset", + "pf-name", ifaceStatus.Name, + "address", ifaceStatus.PciAddress) + continue + } else { + err = s.udevHelper.RemoveUdevRule(ifaceStatus.PciAddress) + if err != nil { + return err + } + } + + if err = s.ResetSriovDevice(ifaceStatus); err != nil { + return err + } + } + } + return nil +} + +func (s *sriov) ConfigSriovDeviceVirtual(iface *sriovnetworkv1.Interface) error { + log.Log.V(2).Info("ConfigSriovDeviceVirtual(): config interface", "address", iface.PciAddress, "config", iface) + // Config VFs + if iface.NumVfs > 0 { + if iface.NumVfs > 1 { + log.Log.Error(nil, "ConfigSriovDeviceVirtual(): in a virtual environment, only one VF per interface", + "numVfs", iface.NumVfs) + return errors.New("NumVfs > 1") + } + if len(iface.VfGroups) != 1 { + log.Log.Error(nil, "ConfigSriovDeviceVirtual(): missing VFGroup") + return errors.New("NumVfs != 1") + } + addr := iface.PciAddress + log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "address", addr) + driver := "" + vfID := 0 + for _, group := range iface.VfGroups { + log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "group", group) + if sriovnetworkv1.IndexInRange(vfID, group.VfRange) { + log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "indexInRange", vfID) + if sriovnetworkv1.StringInArray(group.DeviceType, vars.DpdkDrivers) { + log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "driver", group.DeviceType) + driver = group.DeviceType + } + break + } + } + if driver == "" { + log.Log.V(2).Info("ConfigSriovDeviceVirtual(): bind default") + if err := s.kernelHelper.BindDefaultDriver(addr); err != nil { + log.Log.Error(err, "ConfigSriovDeviceVirtual(): fail to bind default driver", "device", addr) + return err + } + } else { + log.Log.V(2).Info("ConfigSriovDeviceVirtual(): bind driver", "driver", driver) + if err := s.kernelHelper.BindDpdkDriver(addr, driver); err != nil { + log.Log.Error(err, "ConfigSriovDeviceVirtual(): fail to bind driver for device", + "driver", driver, "device", addr) + return err + } + } + } + return nil +} + +func (s *sriov) GetNicSriovMode(pciAddress string) (string, error) { + log.Log.V(2).Info("GetNicSriovMode()", "device", pciAddress) + + devLink, err := s.netlinkLib.DevLinkGetDeviceByName("pci", pciAddress) + if err != nil { + if errors.Is(err, syscall.ENODEV) { + // the device doesn't support devlink + return "", nil + } + return "", err + } + + return devLink.Attrs.Eswitch.Mode, nil +} + +func (s *sriov) SetNicSriovMode(pciAddress string, mode string) error { + log.Log.V(2).Info("SetNicSriovMode()", "device", pciAddress, "mode", mode) + + dev, err := s.netlinkLib.DevLinkGetDeviceByName("pci", pciAddress) + if err != nil { + return err + } + return s.netlinkLib.DevLinkSetEswitchMode(dev, mode) +} + +func (s *sriov) GetLinkType(ifaceStatus sriovnetworkv1.InterfaceExt) string { + log.Log.V(2).Info("GetLinkType()", "device", ifaceStatus.PciAddress) + if ifaceStatus.Name != "" { + link, err := s.netlinkLib.LinkByName(ifaceStatus.Name) + if err != nil { + log.Log.Error(err, "GetLinkType(): failed to get link", "device", ifaceStatus.Name) + return "" + } + linkType := link.Attrs().EncapType + if linkType == "ether" { + return consts.LinkTypeETH + } else if linkType == "infiniband" { + return consts.LinkTypeIB + } + } + + return "" +} diff --git a/pkg/host/internal/sriov/sriov_test.go b/pkg/host/internal/sriov/sriov_test.go new file mode 100644 index 000000000..b380c5644 --- /dev/null +++ b/pkg/host/internal/sriov/sriov_test.go @@ -0,0 +1,99 @@ +package sriov + +import ( + "fmt" + "strconv" + "syscall" + + "github.com/golang/mock/gomock" + "github.com/vishvananda/netlink" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + dputilsMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils/mock" + netlinkMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink/mock" + hostMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/mock" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/helpers" +) + +var _ = Describe("SRIOV", func() { + var ( + s types.SriovInterface + netlinkLibMock *netlinkMockPkg.MockNetlinkLib + dputilsLibMock *dputilsMockPkg.MockDPUtilsLib + hostMock *hostMockPkg.MockHostManagerInterface + + testCtrl *gomock.Controller + + testError = fmt.Errorf("test") + ) + BeforeEach(func() { + testCtrl = gomock.NewController(GinkgoT()) + netlinkLibMock = netlinkMockPkg.NewMockNetlinkLib(testCtrl) + dputilsLibMock = dputilsMockPkg.NewMockDPUtilsLib(testCtrl) + hostMock = hostMockPkg.NewMockHostManagerInterface(testCtrl) + s = New(nil, hostMock, hostMock, hostMock, netlinkLibMock, dputilsLibMock) + }) + + AfterEach(func() { + testCtrl.Finish() + }) + + Context("SetSriovNumVfs", func() { + It("set", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/sys/bus/pci/devices/0000:d8:00.0"}, + Files: map[string][]byte{"/sys/bus/pci/devices/0000:d8:00.0/sriov_numvfs": {}}, + }) + Expect(s.SetSriovNumVfs("0000:d8:00.0", 5)).NotTo(HaveOccurred()) + helpers.GinkgoAssertFileContentsEquals("/sys/bus/pci/devices/0000:d8:00.0/sriov_numvfs", strconv.Itoa(5)) + }) + It("fail - no such device", func() { + Expect(s.SetSriovNumVfs("0000:d8:00.0", 5)).To(HaveOccurred()) + }) + }) + + Context("GetNicSriovMode", func() { + It("devlink returns info", func() { + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return( + &netlink.DevlinkDevice{Attrs: netlink.DevlinkDevAttrs{Eswitch: netlink.DevlinkDevEswitchAttr{Mode: "switchdev"}}}, + nil) + mode, err := s.GetNicSriovMode("0000:d8:00.0") + Expect(err).NotTo(HaveOccurred()) + Expect(mode).To(Equal("switchdev")) + }) + It("devlink returns error", func() { + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return(nil, testError) + _, err := s.GetNicSriovMode("0000:d8:00.0") + Expect(err).To(MatchError(testError)) + }) + It("devlink not supported - fail to get name", func() { + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return(nil, syscall.ENODEV) + mode, err := s.GetNicSriovMode("0000:d8:00.0") + Expect(err).NotTo(HaveOccurred()) + Expect(mode).To(BeEmpty()) + }) + }) + + Context("SetNicSriovMode", func() { + It("set", func() { + testDev := &netlink.DevlinkDevice{} + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return(&netlink.DevlinkDevice{}, nil) + netlinkLibMock.EXPECT().DevLinkSetEswitchMode(testDev, "legacy").Return(nil) + Expect(s.SetNicSriovMode("0000:d8:00.0", "legacy")).NotTo(HaveOccurred()) + }) + It("fail to get dev", func() { + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return(nil, testError) + Expect(s.SetNicSriovMode("0000:d8:00.0", "legacy")).To(MatchError(testError)) + }) + It("fail to set mode", func() { + testDev := &netlink.DevlinkDevice{} + netlinkLibMock.EXPECT().DevLinkGetDeviceByName("pci", "0000:d8:00.0").Return(&netlink.DevlinkDevice{}, nil) + netlinkLibMock.EXPECT().DevLinkSetEswitchMode(testDev, "legacy").Return(testError) + Expect(s.SetNicSriovMode("0000:d8:00.0", "legacy")).To(MatchError(testError)) + }) + }) +}) diff --git a/pkg/host/internal/sriov/suite_test.go b/pkg/host/internal/sriov/suite_test.go new file mode 100644 index 000000000..8f76f0a85 --- /dev/null +++ b/pkg/host/internal/sriov/suite_test.go @@ -0,0 +1,21 @@ +package sriov + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +func TestSriov(t *testing.T) { + log.SetLogger(zap.New( + zap.WriteTo(GinkgoWriter), + zap.Level(zapcore.Level(-2)), + zap.UseDevMode(true))) + RegisterFailHandler(Fail) + RunSpecs(t, "Package SRIOV Suite") +} diff --git a/pkg/host/internal/udev/suite_test.go b/pkg/host/internal/udev/suite_test.go new file mode 100644 index 000000000..34a8820cf --- /dev/null +++ b/pkg/host/internal/udev/suite_test.go @@ -0,0 +1,30 @@ +package udev + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" +) + +func TestUdev(t *testing.T) { + log.SetLogger(zap.New( + zap.WriteTo(GinkgoWriter), + zap.Level(zapcore.Level(-2)), + zap.UseDevMode(true))) + RegisterFailHandler(Fail) + RunSpecs(t, "Package Udev Suite") +} + +var _ = BeforeSuite(func() { + vars.SupportedVfIds = []string{"0x1017", "0x1018"} + DeferCleanup(func() { + vars.SupportedVfIds = []string{} + }) +}) diff --git a/pkg/host/internal/udev/udev.go b/pkg/host/internal/udev/udev.go new file mode 100644 index 000000000..2b4c4c6b3 --- /dev/null +++ b/pkg/host/internal/udev/udev.go @@ -0,0 +1,209 @@ +package udev + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "sigs.k8s.io/controller-runtime/pkg/log" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" +) + +type udev struct { + utilsHelper utils.CmdInterface +} + +func New(utilsHelper utils.CmdInterface) types.UdevInterface { + return &udev{utilsHelper: utilsHelper} +} + +type config struct { + Interfaces []sriovnetworkv1.Interface `json:"interfaces"` +} + +func (u *udev) PrepareNMUdevRule(supportedVfIds []string) error { + log.Log.V(2).Info("PrepareNMUdevRule()") + filePath := filepath.Join(vars.FilesystemRoot, consts.HostUdevRulesFolder, "10-nm-unmanaged.rules") + + // remove the old unmanaged rules file + if _, err := os.Stat(filePath); err == nil { + err = os.Remove(filePath) + if err != nil { + log.Log.Error(err, "failed to remove the network manager global unmanaged rule", + "path", filePath) + } + } + + // create the pf finder script for udev rules + stdout, stderr, err := u.utilsHelper.RunCommand("/bin/bash", filepath.Join(vars.FilesystemRoot, consts.UdevDisableNM)) + if err != nil { + log.Log.Error(err, "PrepareNMUdevRule(): failed to prepare nmUdevRule", "stderr", stderr) + return err + } + log.Log.V(2).Info("PrepareNMUdevRule()", "stdout", stdout) + + //save the device list to use for udev rules + vars.SupportedVfIds = supportedVfIds + return nil +} + +func (u *udev) WriteSwitchdevConfFile(newState *sriovnetworkv1.SriovNetworkNodeState, pfsToSkip map[string]bool) (bool, error) { + cfg := config{} + for _, iface := range newState.Spec.Interfaces { + for _, ifaceStatus := range newState.Status.Interfaces { + if iface.PciAddress != ifaceStatus.PciAddress { + continue + } + + if skip := pfsToSkip[iface.PciAddress]; !skip { + continue + } + + if iface.NumVfs > 0 { + var vfGroups []sriovnetworkv1.VfGroup = nil + ifc, err := sriovnetworkv1.FindInterface(newState.Spec.Interfaces, iface.Name) + if err != nil { + log.Log.Error(err, "WriteSwitchdevConfFile(): fail find interface") + } else { + vfGroups = ifc.VfGroups + } + i := sriovnetworkv1.Interface{ + // Not passing all the contents, since only NumVfs and EswitchMode can be configured by configure-switchdev.sh currently. + Name: iface.Name, + PciAddress: iface.PciAddress, + NumVfs: iface.NumVfs, + Mtu: iface.Mtu, + VfGroups: vfGroups, + } + + if iface.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { + i.EswitchMode = iface.EswitchMode + } + cfg.Interfaces = append(cfg.Interfaces, i) + } + } + } + _, err := os.Stat(consts.SriovHostSwitchDevConfPath) + if err != nil { + if os.IsNotExist(err) { + if len(cfg.Interfaces) == 0 { + return false, nil + } + + // TODO: refactor this function to allow using vars.FilesystemRoot for unit-tests + // Create the sriov-operator folder on the host if it doesn't exist + if _, err := os.Stat(consts.Host + consts.SriovConfBasePath); os.IsNotExist(err) { + err = os.Mkdir(consts.Host+consts.SriovConfBasePath, os.ModeDir) + if err != nil { + log.Log.Error(err, "WriteConfFile(): failed to create sriov-operator folder") + return false, err + } + } + + log.Log.V(2).Info("WriteSwitchdevConfFile(): file not existed, create it") + _, err = os.Create(consts.SriovHostSwitchDevConfPath) + if err != nil { + log.Log.Error(err, "WriteSwitchdevConfFile(): failed to create file") + return false, err + } + } else { + return false, err + } + } + oldContent, err := os.ReadFile(consts.SriovHostSwitchDevConfPath) + if err != nil { + log.Log.Error(err, "WriteSwitchdevConfFile(): failed to read file") + return false, err + } + var newContent []byte + if len(cfg.Interfaces) != 0 { + newContent, err = json.Marshal(cfg) + if err != nil { + log.Log.Error(err, "WriteSwitchdevConfFile(): fail to marshal config") + return false, err + } + } + + if bytes.Equal(newContent, oldContent) { + log.Log.V(2).Info("WriteSwitchdevConfFile(): no update") + return false, nil + } + log.Log.V(2).Info("WriteSwitchdevConfFile(): write to switchdev.conf", "content", newContent) + err = os.WriteFile(consts.SriovHostSwitchDevConfPath, newContent, 0644) + if err != nil { + log.Log.Error(err, "WriteSwitchdevConfFile(): failed to write file") + return false, err + } + return true, nil +} + +// AddUdevRule adds a udev rule that disables network-manager for VFs on the concrete PF +func (u *udev) AddUdevRule(pfPciAddress string) error { + log.Log.V(2).Info("AddUdevRule()", "device", pfPciAddress) + udevRuleContent := fmt.Sprintf(consts.NMUdevRule, strings.Join(vars.SupportedVfIds, "|"), pfPciAddress) + return u.addUdevRule(pfPciAddress, "10-nm-disable", udevRuleContent) +} + +// RemoveUdevRule removes a udev rule that disables network-manager for VFs on the concrete PF +func (u *udev) RemoveUdevRule(pfPciAddress string) error { + log.Log.V(2).Info("RemoveUdevRule()", "device", pfPciAddress) + return u.removeUdevRule(pfPciAddress, "10-nm-disable") +} + +// AddVfRepresentorUdevRule adds udev rule that renames VF representors on the concrete PF +func (u *udev) AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort string) error { + log.Log.V(2).Info("AddVfRepresentorUdevRule()", + "device", pfPciAddress, "name", pfName, "switch", pfSwitchID, "port", pfSwitchPort) + udevRuleContent := fmt.Sprintf(consts.SwitchdevUdevRule, pfSwitchID, strings.TrimPrefix(pfSwitchPort, "p"), pfName) + return u.addUdevRule(pfPciAddress, "20-switchdev", udevRuleContent) +} + +// RemoveVfRepresentorUdevRule removes udev rule that renames VF representors on the concrete PF +func (u *udev) RemoveVfRepresentorUdevRule(pfPciAddress string) error { + log.Log.V(2).Info("RemoveVfRepresentorUdevRule()", "device", pfPciAddress) + return u.removeUdevRule(pfPciAddress, "20-switchdev") +} + +func (u *udev) addUdevRule(pfPciAddress, ruleName, ruleContent string) error { + log.Log.V(2).Info("addUdevRule()", "device", pfPciAddress, "rule", ruleName) + rulePath := u.getRuleFolderPath() + err := os.MkdirAll(rulePath, os.ModePerm) + if err != nil && !os.IsExist(err) { + log.Log.Error(err, "ensureUdevRulePathExist(): failed to create dir", "path", rulePath) + return err + } + filePath := u.getRulePathForPF(ruleName, pfPciAddress) + if err := os.WriteFile(filePath, []byte(ruleContent), 0666); err != nil { + log.Log.Error(err, "addUdevRule(): fail to write file", "path", filePath) + return err + } + return nil +} + +func (u *udev) removeUdevRule(pfPciAddress, ruleName string) error { + log.Log.V(2).Info("removeUdevRule()", "device", pfPciAddress, "rule", ruleName) + rulePath := u.getRulePathForPF(ruleName, pfPciAddress) + err := os.Remove(rulePath) + if err != nil && !os.IsNotExist(err) { + log.Log.Error(err, "removeUdevRule(): fail to remove rule file", "path", rulePath) + return err + } + return nil +} + +func (u *udev) getRuleFolderPath() string { + return filepath.Join(vars.FilesystemRoot, consts.UdevRulesFolder) +} + +func (u *udev) getRulePathForPF(ruleName, pfPciAddress string) string { + return path.Join(u.getRuleFolderPath(), fmt.Sprintf("%s-%s.rules", ruleName, pfPciAddress)) +} diff --git a/pkg/host/internal/udev/udev_test.go b/pkg/host/internal/udev/udev_test.go new file mode 100644 index 000000000..d5f222385 --- /dev/null +++ b/pkg/host/internal/udev/udev_test.go @@ -0,0 +1,117 @@ +package udev + +import ( + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/helpers" +) + +const ( + testExpectedNMUdevRule = `SUBSYSTEM=="net", ACTION=="add|change|move", ` + + `ATTRS{device}=="0x1017|0x1018", ` + + `IMPORT{program}="/etc/udev/disable-nm-sriov.sh $env{INTERFACE} 0000:d8:00.0"` + testExpectedSwitchdevUdevRule = `SUBSYSTEM=="net", ACTION=="add|move", ` + + `ATTRS{phys_switch_id}=="7cfe90ff2cc0", ` + + `ATTR{phys_port_name}=="pf0vf*", IMPORT{program}="/etc/udev/switchdev-vf-link-name.sh $attr{phys_port_name}", ` + + `NAME="enp216s0f0np0_$env{NUMBER}"` +) + +var _ = Describe("UDEV", func() { + var ( + s types.UdevInterface + ) + BeforeEach(func() { + s = New(nil) + }) + Context("AddUdevRule", func() { + It("Created", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{}) + Expect(s.AddUdevRule("0000:d8:00.0")).To(BeNil()) + helpers.GinkgoAssertFileContentsEquals( + "/etc/udev/rules.d/10-nm-disable-0000:d8:00.0.rules", + testExpectedNMUdevRule) + }) + It("Overwrite", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + Files: map[string][]byte{ + "/etc/udev/rules.d/10-nm-disable-0000:d8:00.0.rules": []byte("something"), + }, + }) + Expect(s.AddUdevRule("0000:d8:00.0")).To(BeNil()) + helpers.GinkgoAssertFileContentsEquals( + "/etc/udev/rules.d/10-nm-disable-0000:d8:00.0.rules", + testExpectedNMUdevRule) + }) + }) + Context("RemoveUdevRule", func() { + It("Exist", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + Files: map[string][]byte{ + "/etc/udev/rules.d/10-nm-disable-0000:d8:00.0.rules": []byte(testExpectedNMUdevRule), + }, + }) + Expect(s.RemoveUdevRule("0000:d8:00.0")).To(BeNil()) + _, err := os.Stat(filepath.Join(vars.FilesystemRoot, + "/etc/udev/rules.d/10-nm-disable-0000:d8:00.0.rules")) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + It("Not found", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + }) + Expect(s.RemoveUdevRule("0000:d8:00.0")).To(BeNil()) + }) + }) + Context("AddVfRepresentorUdevRule", func() { + It("Created", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{}) + Expect(s.AddVfRepresentorUdevRule("0000:d8:00.0", + "enp216s0f0np0", "7cfe90ff2cc0", "p0")).To(BeNil()) + helpers.GinkgoAssertFileContentsEquals( + "/etc/udev/rules.d/20-switchdev-0000:d8:00.0.rules", + testExpectedSwitchdevUdevRule) + }) + It("Overwrite", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + Files: map[string][]byte{ + "/etc/udev/rules.d/20-switchdev-0000:d8:00.0.rules": []byte("something"), + }, + }) + Expect(s.AddVfRepresentorUdevRule("0000:d8:00.0", + "enp216s0f0np0", "7cfe90ff2cc0", "p0")).To(BeNil()) + helpers.GinkgoAssertFileContentsEquals( + "/etc/udev/rules.d/20-switchdev-0000:d8:00.0.rules", + testExpectedSwitchdevUdevRule) + }) + }) + Context("RemoveVfRepresentorUdevRule", func() { + It("Exist", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + Files: map[string][]byte{ + "/etc/udev/rules.d/20-switchdev-0000:d8:00.0.rules": []byte(testExpectedSwitchdevUdevRule), + }, + }) + Expect(s.RemoveVfRepresentorUdevRule("0000:d8:00.0")).To(BeNil()) + _, err := os.Stat(filepath.Join(vars.FilesystemRoot, + "/etc/udev/rules.d/20-switchdev-0000:d8:00.0.rules")) + Expect(os.IsNotExist(err)).To(BeTrue()) + }) + It("Not found", func() { + helpers.GinkgoConfigureFakeFS(&fakefilesystem.FS{ + Dirs: []string{"/etc/udev/rules.d"}, + }) + Expect(s.RemoveVfRepresentorUdevRule("0000:d8:00.0")).To(BeNil()) + }) + }) +}) diff --git a/pkg/host/internal/vdpa/suite_test.go b/pkg/host/internal/vdpa/suite_test.go new file mode 100644 index 000000000..5863f7f27 --- /dev/null +++ b/pkg/host/internal/vdpa/suite_test.go @@ -0,0 +1,21 @@ +package vdpa + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +func TestVdpa(t *testing.T) { + log.SetLogger(zap.New( + zap.WriteTo(GinkgoWriter), + zap.Level(zapcore.Level(-2)), + zap.UseDevMode(true))) + RegisterFailHandler(Fail) + RunSpecs(t, "Package VDPA Suite") +} diff --git a/pkg/host/internal/vdpa/vdpa.go b/pkg/host/internal/vdpa/vdpa.go new file mode 100644 index 000000000..010098f8b --- /dev/null +++ b/pkg/host/internal/vdpa/vdpa.go @@ -0,0 +1,131 @@ +package vdpa + +import ( + "errors" + "fmt" + "syscall" + + "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" + "sigs.k8s.io/controller-runtime/pkg/log" + + constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/govdpa" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" +) + +type vdpa struct { + kernel types.KernelInterface + vdpaLib govdpa.GoVdpaLib +} + +func New(k types.KernelInterface, vdpaLib govdpa.GoVdpaLib) types.VdpaInterface { + return &vdpa{kernel: k, vdpaLib: vdpaLib} +} + +// CreateVDPADevice creates VDPA device for VF with required type, +// pciAddr - PCI address of the VF +// vdpaType - type of the VDPA device to create: virtio of vhost +func (v *vdpa) CreateVDPADevice(pciAddr, vdpaType string) error { + log.Log.V(2).Info("CreateVDPADevice(): create VDPA device for VF", + "device", pciAddr, "vdpaType", vdpaType) + expectedDriver := vdpaTypeToDriver(vdpaType) + if expectedDriver == "" { + return fmt.Errorf("unknown VDPA device type: %s", vdpaType) + } + expectedVDPAName := generateVDPADevName(pciAddr) + _, err := v.vdpaLib.GetVdpaDevice(expectedVDPAName) + if err != nil { + if !errors.Is(err, syscall.ENODEV) { + log.Log.Error(err, "CreateVDPADevice(): fail to check if VDPA device exist", + "device", pciAddr, "vdpaDev", expectedVDPAName) + return err + } + if err := v.vdpaLib.AddVdpaDevice("pci/"+pciAddr, expectedVDPAName); err != nil { + log.Log.Error(err, "CreateVDPADevice(): fail to create VDPA device", + "device", pciAddr, "vdpaDev", expectedVDPAName) + return err + } + } + err = v.kernel.BindDriverByBusAndDevice(constants.BusVdpa, expectedVDPAName, expectedDriver) + if err != nil { + log.Log.Error(err, "CreateVDPADevice(): fail to bind VDPA device to the driver", + "device", pciAddr, "vdpaDev", expectedVDPAName, "driver", expectedDriver) + return err + } + return nil +} + +// DeleteVDPADevice removes VDPA device for provided pci address +// pciAddr - PCI address of the VF +func (v *vdpa) DeleteVDPADevice(pciAddr string) error { + log.Log.V(2).Info("DeleteVDPADevice(): delete VDPA device for VF", + "device", pciAddr) + expectedVDPAName := generateVDPADevName(pciAddr) + if err := v.vdpaLib.DeleteVdpaDevice(expectedVDPAName); err != nil { + if errors.Is(err, syscall.ENODEV) { + log.Log.V(2).Info("DeleteVDPADevice(): VDPA device not found", + "device", pciAddr, "name", expectedVDPAName) + return nil + } + log.Log.Error(err, "DeleteVDPADevice(): fail to remove VDPA device", + "device", pciAddr, "name", expectedVDPAName) + return err + } + return nil +} + +// DiscoverVDPAType returns type of existing VDPA device for VF, +// returns empty string if VDPA device not found or unknown driver is in use +// pciAddr - PCI address of the VF +func (v *vdpa) DiscoverVDPAType(pciAddr string) string { + expectedVDPADevName := generateVDPADevName(pciAddr) + vdpaDev, err := v.vdpaLib.GetVdpaDevice(expectedVDPADevName) + if err != nil { + if errors.Is(err, syscall.ENODEV) { + log.Log.V(2).Info("discoverVDPAType(): VDPA device for VF not found", "device", pciAddr) + return "" + } + log.Log.Error(err, "getVfInfo(): unable to get VF VDPA devices", "device", pciAddr) + return "" + } + driverName := vdpaDev.Driver() + if driverName == "" { + log.Log.V(2).Info("discoverVDPAType(): VDPA device has no driver", "device", pciAddr) + return "" + } + vdpaType := vdpaDriverToType(driverName) + if vdpaType == "" { + log.Log.Error(nil, "getVfInfo(): WARNING: unknown VDPA device type for VF, ignore", + "device", pciAddr, "driver", driverName) + } + return vdpaType +} + +// generates predictable name for VDPA device, example: vpda:0000:03:00.1 +func generateVDPADevName(pciAddr string) string { + return "vdpa:" + pciAddr +} + +// vdpa type to driver name conversion +func vdpaTypeToDriver(vdpaType string) string { + switch vdpaType { + case constants.VdpaTypeVhost: + return kvdpa.VhostVdpaDriver + case constants.VdpaTypeVirtio: + return kvdpa.VirtioVdpaDriver + default: + return "" + } +} + +// vdpa driver name to type conversion +func vdpaDriverToType(driver string) string { + switch driver { + case kvdpa.VhostVdpaDriver: + return constants.VdpaTypeVhost + case kvdpa.VirtioVdpaDriver: + return constants.VdpaTypeVirtio + default: + return "" + } +} diff --git a/pkg/host/internal/vdpa/vdpa_test.go b/pkg/host/internal/vdpa/vdpa_test.go new file mode 100644 index 000000000..3c668aa44 --- /dev/null +++ b/pkg/host/internal/vdpa/vdpa_test.go @@ -0,0 +1,115 @@ +package vdpa + +import ( + "fmt" + "syscall" + + "github.com/golang/mock/gomock" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + govdpaMock "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/govdpa/mock" + hostMock "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/mock" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" +) + +var _ = Describe("VDPA", func() { + var ( + v types.VdpaInterface + libMock *govdpaMock.MockGoVdpaLib + vdpaDevMock *govdpaMock.MockVdpaDevice + kernelMock *hostMock.MockHostManagerInterface + + testCtrl *gomock.Controller + testErr = fmt.Errorf("test-error") + ) + BeforeEach(func() { + testCtrl = gomock.NewController(GinkgoT()) + libMock = govdpaMock.NewMockGoVdpaLib(testCtrl) + vdpaDevMock = govdpaMock.NewMockVdpaDevice(testCtrl) + kernelMock = hostMock.NewMockHostManagerInterface(testCtrl) + v = New(kernelMock, libMock) + }) + AfterEach(func() { + testCtrl.Finish() + }) + Context("CreateVDPADevice", func() { + callFunc := func() error { + return v.CreateVDPADevice("0000:d8:00.2", constants.VdpaTypeVhost) + } + It("Created", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(nil, syscall.ENODEV) + libMock.EXPECT().AddVdpaDevice("pci/"+"0000:d8:00.2", "vdpa:0000:d8:00.2").Return(nil) + kernelMock.EXPECT().BindDriverByBusAndDevice(consts.BusVdpa, "vdpa:0000:d8:00.2", "vhost_vdpa").Return(nil) + Expect(callFunc()).NotTo(HaveOccurred()) + }) + It("Already exist", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + kernelMock.EXPECT().BindDriverByBusAndDevice(consts.BusVdpa, "vdpa:0000:d8:00.2", "vhost_vdpa").Return(nil) + Expect(callFunc()).NotTo(HaveOccurred()) + }) + It("Fail to Get device", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(nil, testErr) + Expect(callFunc()).To(MatchError(testErr)) + }) + It("Fail to Create device", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(nil, syscall.ENODEV) + libMock.EXPECT().AddVdpaDevice("pci/"+"0000:d8:00.2", "vdpa:0000:d8:00.2").Return(testErr) + Expect(callFunc()).To(MatchError(testErr)) + }) + It("Fail to Bind device", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + kernelMock.EXPECT().BindDriverByBusAndDevice(consts.BusVdpa, "vdpa:0000:d8:00.2", "vhost_vdpa").Return(testErr) + Expect(callFunc()).To(MatchError(testErr)) + }) + }) + Context("DeleteVDPADevice", func() { + callFunc := func() error { + return v.DeleteVDPADevice("0000:d8:00.2") + } + It("Removed", func() { + libMock.EXPECT().DeleteVdpaDevice("vdpa:0000:d8:00.2").Return(nil) + Expect(callFunc()).NotTo(HaveOccurred()) + }) + It("Not found", func() { + libMock.EXPECT().DeleteVdpaDevice("vdpa:0000:d8:00.2").Return(syscall.ENODEV) + Expect(callFunc()).NotTo(HaveOccurred()) + }) + It("Fail to delete device", func() { + libMock.EXPECT().DeleteVdpaDevice("vdpa:0000:d8:00.2").Return(testErr) + Expect(callFunc()).To(MatchError(testErr)) + }) + }) + Context("DiscoverVDPAType", func() { + callFunc := func() string { + return v.DiscoverVDPAType("0000:d8:00.2") + } + It("No device", func() { + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(nil, syscall.ENODEV) + Expect(callFunc()).To(BeEmpty()) + }) + It("No driver", func() { + vdpaDevMock.EXPECT().Driver().Return("") + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + Expect(callFunc()).To(BeEmpty()) + }) + It("Unknown driver", func() { + vdpaDevMock.EXPECT().Driver().Return("something") + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + Expect(callFunc()).To(BeEmpty()) + }) + It("Vhost driver", func() { + vdpaDevMock.EXPECT().Driver().Return("vhost_vdpa") + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + Expect(callFunc()).To(Equal("vhost")) + }) + It("Virtio driver", func() { + vdpaDevMock.EXPECT().Driver().Return("virtio_vdpa") + libMock.EXPECT().GetVdpaDevice("vdpa:0000:d8:00.2").Return(vdpaDevMock, nil) + Expect(callFunc()).To(Equal("virtio")) + }) + }) +}) diff --git a/pkg/host/manager.go b/pkg/host/manager.go new file mode 100644 index 000000000..83399ab90 --- /dev/null +++ b/pkg/host/manager.go @@ -0,0 +1,57 @@ +package host + +import ( + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/kernel" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/govdpa" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/network" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/service" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/sriov" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/udev" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/vdpa" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" +) + +// Contains all the host manipulation functions +// +//go:generate ../../bin/mockgen -destination mock/mock_host.go -source manager.go +type HostManagerInterface interface { + types.KernelInterface + types.NetworkInterface + types.ServiceInterface + types.UdevInterface + types.SriovInterface + types.VdpaInterface +} + +type hostManager struct { + utils.CmdInterface + types.KernelInterface + types.NetworkInterface + types.ServiceInterface + types.UdevInterface + types.SriovInterface + types.VdpaInterface +} + +func NewHostManager(utilsInterface utils.CmdInterface) HostManagerInterface { + dpUtils := dputils.New() + k := kernel.New(utilsInterface) + n := network.New(utilsInterface, dpUtils) + sv := service.New(utilsInterface) + u := udev.New(utilsInterface) + sr := sriov.New(utilsInterface, k, n, u, netlink.New(), dpUtils) + v := vdpa.New(k, govdpa.New()) + + return &hostManager{ + utilsInterface, + k, + n, + sv, + u, + sr, + v, + } +} diff --git a/pkg/host/mock/mock_host.go b/pkg/host/mock/mock_host.go index e09bfd3c8..ddba8626e 100644 --- a/pkg/host/mock/mock_host.go +++ b/pkg/host/mock/mock_host.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: host.go +// Source: manager.go // Package mock_host is a generated GoMock package. package mock_host @@ -7,7 +7,13 @@ package mock_host import ( reflect "reflect" + unit "github.com/coreos/go-systemd/v22/unit" gomock "github.com/golang/mock/gomock" + ghw "github.com/jaypipes/ghw" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + store "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store" + types "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" + netlink "github.com/vishvananda/netlink" ) // MockHostManagerInterface is a mock of HostManagerInterface interface. @@ -33,19 +39,203 @@ func (m *MockHostManagerInterface) EXPECT() *MockHostManagerInterfaceMockRecorde return m.recorder } +// AddUdevRule mocks base method. +func (m *MockHostManagerInterface) AddUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddUdevRule indicates an expected call of AddUdevRule. +func (mr *MockHostManagerInterfaceMockRecorder) AddUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUdevRule", reflect.TypeOf((*MockHostManagerInterface)(nil).AddUdevRule), pfPciAddress) +} + +// AddVfRepresentorUdevRule mocks base method. +func (m *MockHostManagerInterface) AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddVfRepresentorUdevRule", pfPciAddress, pfName, pfSwitchID, pfSwitchPort) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddVfRepresentorUdevRule indicates an expected call of AddVfRepresentorUdevRule. +func (mr *MockHostManagerInterfaceMockRecorder) AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVfRepresentorUdevRule", reflect.TypeOf((*MockHostManagerInterface)(nil).AddVfRepresentorUdevRule), pfPciAddress, pfName, pfSwitchID, pfSwitchPort) +} + +// BindDefaultDriver mocks base method. +func (m *MockHostManagerInterface) BindDefaultDriver(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDefaultDriver", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDefaultDriver indicates an expected call of BindDefaultDriver. +func (mr *MockHostManagerInterfaceMockRecorder) BindDefaultDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDefaultDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).BindDefaultDriver), pciAddr) +} + +// BindDpdkDriver mocks base method. +func (m *MockHostManagerInterface) BindDpdkDriver(pciAddr, driver string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDpdkDriver", pciAddr, driver) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDpdkDriver indicates an expected call of BindDpdkDriver. +func (mr *MockHostManagerInterfaceMockRecorder) BindDpdkDriver(pciAddr, driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDpdkDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).BindDpdkDriver), pciAddr, driver) +} + +// BindDriverByBusAndDevice mocks base method. +func (m *MockHostManagerInterface) BindDriverByBusAndDevice(bus, device, driver string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDriverByBusAndDevice", bus, device, driver) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDriverByBusAndDevice indicates an expected call of BindDriverByBusAndDevice. +func (mr *MockHostManagerInterfaceMockRecorder) BindDriverByBusAndDevice(bus, device, driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDriverByBusAndDevice", reflect.TypeOf((*MockHostManagerInterface)(nil).BindDriverByBusAndDevice), bus, device, driver) +} + +// CompareServices mocks base method. +func (m *MockHostManagerInterface) CompareServices(serviceA, serviceB *types.Service) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CompareServices", serviceA, serviceB) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CompareServices indicates an expected call of CompareServices. +func (mr *MockHostManagerInterfaceMockRecorder) CompareServices(serviceA, serviceB interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareServices", reflect.TypeOf((*MockHostManagerInterface)(nil).CompareServices), serviceA, serviceB) +} + +// ConfigSriovDevice mocks base method. +func (m *MockHostManagerInterface) ConfigSriovDevice(iface *v1.Interface, ifaceStatus *v1.InterfaceExt) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovDevice", iface, ifaceStatus) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovDevice indicates an expected call of ConfigSriovDevice. +func (mr *MockHostManagerInterfaceMockRecorder) ConfigSriovDevice(iface, ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovDevice", reflect.TypeOf((*MockHostManagerInterface)(nil).ConfigSriovDevice), iface, ifaceStatus) +} + +// ConfigSriovDeviceVirtual mocks base method. +func (m *MockHostManagerInterface) ConfigSriovDeviceVirtual(iface *v1.Interface) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovDeviceVirtual", iface) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovDeviceVirtual indicates an expected call of ConfigSriovDeviceVirtual. +func (mr *MockHostManagerInterfaceMockRecorder) ConfigSriovDeviceVirtual(iface interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovDeviceVirtual", reflect.TypeOf((*MockHostManagerInterface)(nil).ConfigSriovDeviceVirtual), iface) +} + +// ConfigSriovInterfaces mocks base method. +func (m *MockHostManagerInterface) ConfigSriovInterfaces(storeManager store.ManagerInterface, interfaces []v1.Interface, ifaceStatuses []v1.InterfaceExt, pfsToConfig map[string]bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigSriovInterfaces", storeManager, interfaces, ifaceStatuses, pfsToConfig) + ret0, _ := ret[0].(error) + return ret0 +} + +// ConfigSriovInterfaces indicates an expected call of ConfigSriovInterfaces. +func (mr *MockHostManagerInterfaceMockRecorder) ConfigSriovInterfaces(storeManager, interfaces, ifaceStatuses, pfsToConfig interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigSriovInterfaces", reflect.TypeOf((*MockHostManagerInterface)(nil).ConfigSriovInterfaces), storeManager, interfaces, ifaceStatuses, pfsToConfig) +} + +// CreateVDPADevice mocks base method. +func (m *MockHostManagerInterface) CreateVDPADevice(pciAddr, vdpaType string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateVDPADevice", pciAddr, vdpaType) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateVDPADevice indicates an expected call of CreateVDPADevice. +func (mr *MockHostManagerInterfaceMockRecorder) CreateVDPADevice(pciAddr, vdpaType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVDPADevice", reflect.TypeOf((*MockHostManagerInterface)(nil).CreateVDPADevice), pciAddr, vdpaType) +} + +// DeleteVDPADevice mocks base method. +func (m *MockHostManagerInterface) DeleteVDPADevice(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteVDPADevice", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteVDPADevice indicates an expected call of DeleteVDPADevice. +func (mr *MockHostManagerInterfaceMockRecorder) DeleteVDPADevice(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVDPADevice", reflect.TypeOf((*MockHostManagerInterface)(nil).DeleteVDPADevice), pciAddr) +} + +// DiscoverSriovDevices mocks base method. +func (m *MockHostManagerInterface) DiscoverSriovDevices(storeManager store.ManagerInterface) ([]v1.InterfaceExt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverSriovDevices", storeManager) + ret0, _ := ret[0].([]v1.InterfaceExt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DiscoverSriovDevices indicates an expected call of DiscoverSriovDevices. +func (mr *MockHostManagerInterfaceMockRecorder) DiscoverSriovDevices(storeManager interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverSriovDevices", reflect.TypeOf((*MockHostManagerInterface)(nil).DiscoverSriovDevices), storeManager) +} + +// DiscoverVDPAType mocks base method. +func (m *MockHostManagerInterface) DiscoverVDPAType(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverVDPAType", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// DiscoverVDPAType indicates an expected call of DiscoverVDPAType. +func (mr *MockHostManagerInterfaceMockRecorder) DiscoverVDPAType(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverVDPAType", reflect.TypeOf((*MockHostManagerInterface)(nil).DiscoverVDPAType), pciAddr) +} + // EnableRDMA mocks base method. -func (m *MockHostManagerInterface) EnableRDMA(arg0, arg1, arg2 string) (bool, error) { +func (m *MockHostManagerInterface) EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnableRDMA", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "EnableRDMA", conditionFilePath, serviceName, packageManager) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // EnableRDMA indicates an expected call of EnableRDMA. -func (mr *MockHostManagerInterfaceMockRecorder) EnableRDMA(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockHostManagerInterfaceMockRecorder) EnableRDMA(conditionFilePath, serviceName, packageManager interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRDMA", reflect.TypeOf((*MockHostManagerInterface)(nil).EnableRDMA), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRDMA", reflect.TypeOf((*MockHostManagerInterface)(nil).EnableRDMA), conditionFilePath, serviceName, packageManager) } // EnableRDMAOnRHELMachine mocks base method. @@ -63,6 +253,106 @@ func (mr *MockHostManagerInterfaceMockRecorder) EnableRDMAOnRHELMachine() *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRDMAOnRHELMachine", reflect.TypeOf((*MockHostManagerInterface)(nil).EnableRDMAOnRHELMachine)) } +// EnableService mocks base method. +func (m *MockHostManagerInterface) EnableService(service *types.Service) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnableService", service) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnableService indicates an expected call of EnableService. +func (mr *MockHostManagerInterfaceMockRecorder) EnableService(service interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableService", reflect.TypeOf((*MockHostManagerInterface)(nil).EnableService), service) +} + +// GetCurrentKernelArgs mocks base method. +func (m *MockHostManagerInterface) GetCurrentKernelArgs() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCurrentKernelArgs") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCurrentKernelArgs indicates an expected call of GetCurrentKernelArgs. +func (mr *MockHostManagerInterfaceMockRecorder) GetCurrentKernelArgs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentKernelArgs", reflect.TypeOf((*MockHostManagerInterface)(nil).GetCurrentKernelArgs)) +} + +// GetLinkType mocks base method. +func (m *MockHostManagerInterface) GetLinkType(ifaceStatus v1.InterfaceExt) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLinkType", ifaceStatus) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetLinkType indicates an expected call of GetLinkType. +func (mr *MockHostManagerInterfaceMockRecorder) GetLinkType(ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLinkType", reflect.TypeOf((*MockHostManagerInterface)(nil).GetLinkType), ifaceStatus) +} + +// GetNetDevLinkSpeed mocks base method. +func (m *MockHostManagerInterface) GetNetDevLinkSpeed(name string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetDevLinkSpeed", name) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetDevLinkSpeed indicates an expected call of GetNetDevLinkSpeed. +func (mr *MockHostManagerInterfaceMockRecorder) GetNetDevLinkSpeed(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetDevLinkSpeed", reflect.TypeOf((*MockHostManagerInterface)(nil).GetNetDevLinkSpeed), name) +} + +// GetNetDevMac mocks base method. +func (m *MockHostManagerInterface) GetNetDevMac(name string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetDevMac", name) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetDevMac indicates an expected call of GetNetDevMac. +func (mr *MockHostManagerInterfaceMockRecorder) GetNetDevMac(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetDevMac", reflect.TypeOf((*MockHostManagerInterface)(nil).GetNetDevMac), name) +} + +// GetNetdevMTU mocks base method. +func (m *MockHostManagerInterface) GetNetdevMTU(pciAddr string) int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetdevMTU", pciAddr) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetNetdevMTU indicates an expected call of GetNetdevMTU. +func (mr *MockHostManagerInterfaceMockRecorder) GetNetdevMTU(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetdevMTU", reflect.TypeOf((*MockHostManagerInterface)(nil).GetNetdevMTU), pciAddr) +} + +// GetNicSriovMode mocks base method. +func (m *MockHostManagerInterface) GetNicSriovMode(pciAddr string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNicSriovMode", pciAddr) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNicSriovMode indicates an expected call of GetNicSriovMode. +func (mr *MockHostManagerInterfaceMockRecorder) GetNicSriovMode(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNicSriovMode", reflect.TypeOf((*MockHostManagerInterface)(nil).GetNicSriovMode), pciAddr) +} + // GetOSPrettyName mocks base method. func (m *MockHostManagerInterface) GetOSPrettyName() (string, error) { m.ctrl.T.Helper() @@ -78,18 +368,77 @@ func (mr *MockHostManagerInterfaceMockRecorder) GetOSPrettyName() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOSPrettyName", reflect.TypeOf((*MockHostManagerInterface)(nil).GetOSPrettyName)) } +// GetPhysPortName mocks base method. +func (m *MockHostManagerInterface) GetPhysPortName(name string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhysPortName", name) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPhysPortName indicates an expected call of GetPhysPortName. +func (mr *MockHostManagerInterfaceMockRecorder) GetPhysPortName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhysPortName", reflect.TypeOf((*MockHostManagerInterface)(nil).GetPhysPortName), name) +} + +// GetPhysSwitchID mocks base method. +func (m *MockHostManagerInterface) GetPhysSwitchID(name string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhysSwitchID", name) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPhysSwitchID indicates an expected call of GetPhysSwitchID. +func (mr *MockHostManagerInterfaceMockRecorder) GetPhysSwitchID(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhysSwitchID", reflect.TypeOf((*MockHostManagerInterface)(nil).GetPhysSwitchID), name) +} + +// GetVfInfo mocks base method. +func (m *MockHostManagerInterface) GetVfInfo(pciAddr string, devices []*ghw.PCIDevice) v1.VirtualFunction { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVfInfo", pciAddr, devices) + ret0, _ := ret[0].(v1.VirtualFunction) + return ret0 +} + +// GetVfInfo indicates an expected call of GetVfInfo. +func (mr *MockHostManagerInterfaceMockRecorder) GetVfInfo(pciAddr, devices interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVfInfo", reflect.TypeOf((*MockHostManagerInterface)(nil).GetVfInfo), pciAddr, devices) +} + +// HasDriver mocks base method. +func (m *MockHostManagerInterface) HasDriver(pciAddr string) (bool, string) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasDriver", pciAddr) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(string) + return ret0, ret1 +} + +// HasDriver indicates an expected call of HasDriver. +func (mr *MockHostManagerInterfaceMockRecorder) HasDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).HasDriver), pciAddr) +} + // InstallRDMA mocks base method. -func (m *MockHostManagerInterface) InstallRDMA(arg0 string) error { +func (m *MockHostManagerInterface) InstallRDMA(packageManager string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InstallRDMA", arg0) + ret := m.ctrl.Call(m, "InstallRDMA", packageManager) ret0, _ := ret[0].(error) return ret0 } // InstallRDMA indicates an expected call of InstallRDMA. -func (mr *MockHostManagerInterfaceMockRecorder) InstallRDMA(arg0 interface{}) *gomock.Call { +func (mr *MockHostManagerInterfaceMockRecorder) InstallRDMA(packageManager interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstallRDMA", reflect.TypeOf((*MockHostManagerInterface)(nil).InstallRDMA), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstallRDMA", reflect.TypeOf((*MockHostManagerInterface)(nil).InstallRDMA), packageManager) } // IsCoreOS mocks base method. @@ -107,19 +456,47 @@ func (mr *MockHostManagerInterfaceMockRecorder) IsCoreOS() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCoreOS", reflect.TypeOf((*MockHostManagerInterface)(nil).IsCoreOS)) } +// IsKernelArgsSet mocks base method. +func (m *MockHostManagerInterface) IsKernelArgsSet(cmdLine, karg string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsKernelArgsSet", cmdLine, karg) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsKernelArgsSet indicates an expected call of IsKernelArgsSet. +func (mr *MockHostManagerInterfaceMockRecorder) IsKernelArgsSet(cmdLine, karg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelArgsSet", reflect.TypeOf((*MockHostManagerInterface)(nil).IsKernelArgsSet), cmdLine, karg) +} + +// IsKernelLockdownMode mocks base method. +func (m *MockHostManagerInterface) IsKernelLockdownMode() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsKernelLockdownMode") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsKernelLockdownMode indicates an expected call of IsKernelLockdownMode. +func (mr *MockHostManagerInterfaceMockRecorder) IsKernelLockdownMode() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelLockdownMode", reflect.TypeOf((*MockHostManagerInterface)(nil).IsKernelLockdownMode)) +} + // IsKernelModuleLoaded mocks base method. -func (m *MockHostManagerInterface) IsKernelModuleLoaded(arg0 string) (bool, error) { +func (m *MockHostManagerInterface) IsKernelModuleLoaded(name string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsKernelModuleLoaded", arg0) + ret := m.ctrl.Call(m, "IsKernelModuleLoaded", name) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsKernelModuleLoaded indicates an expected call of IsKernelModuleLoaded. -func (mr *MockHostManagerInterfaceMockRecorder) IsKernelModuleLoaded(arg0 interface{}) *gomock.Call { +func (mr *MockHostManagerInterfaceMockRecorder) IsKernelModuleLoaded(name interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelModuleLoaded", reflect.TypeOf((*MockHostManagerInterface)(nil).IsKernelModuleLoaded), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsKernelModuleLoaded", reflect.TypeOf((*MockHostManagerInterface)(nil).IsKernelModuleLoaded), name) } // IsRHELSystem mocks base method. @@ -137,6 +514,50 @@ func (mr *MockHostManagerInterfaceMockRecorder) IsRHELSystem() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRHELSystem", reflect.TypeOf((*MockHostManagerInterface)(nil).IsRHELSystem)) } +// IsServiceEnabled mocks base method. +func (m *MockHostManagerInterface) IsServiceEnabled(servicePath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsServiceEnabled", servicePath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsServiceEnabled indicates an expected call of IsServiceEnabled. +func (mr *MockHostManagerInterfaceMockRecorder) IsServiceEnabled(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsServiceEnabled", reflect.TypeOf((*MockHostManagerInterface)(nil).IsServiceEnabled), servicePath) +} + +// IsServiceExist mocks base method. +func (m *MockHostManagerInterface) IsServiceExist(servicePath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsServiceExist", servicePath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsServiceExist indicates an expected call of IsServiceExist. +func (mr *MockHostManagerInterfaceMockRecorder) IsServiceExist(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsServiceExist", reflect.TypeOf((*MockHostManagerInterface)(nil).IsServiceExist), servicePath) +} + +// IsSwitchdev mocks base method. +func (m *MockHostManagerInterface) IsSwitchdev(name string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsSwitchdev", name) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsSwitchdev indicates an expected call of IsSwitchdev. +func (mr *MockHostManagerInterfaceMockRecorder) IsSwitchdev(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSwitchdev", reflect.TypeOf((*MockHostManagerInterface)(nil).IsSwitchdev), name) +} + // IsUbuntuSystem mocks base method. func (m *MockHostManagerInterface) IsUbuntuSystem() (bool, error) { m.ctrl.T.Helper() @@ -171,6 +592,20 @@ func (mr *MockHostManagerInterfaceMockRecorder) LoadKernelModule(name interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadKernelModule", reflect.TypeOf((*MockHostManagerInterface)(nil).LoadKernelModule), varargs...) } +// PrepareNMUdevRule mocks base method. +func (m *MockHostManagerInterface) PrepareNMUdevRule(supportedVfIds []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrepareNMUdevRule", supportedVfIds) + ret0, _ := ret[0].(error) + return ret0 +} + +// PrepareNMUdevRule indicates an expected call of PrepareNMUdevRule. +func (mr *MockHostManagerInterfaceMockRecorder) PrepareNMUdevRule(supportedVfIds interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareNMUdevRule", reflect.TypeOf((*MockHostManagerInterface)(nil).PrepareNMUdevRule), supportedVfIds) +} + // RdmaIsLoaded mocks base method. func (m *MockHostManagerInterface) RdmaIsLoaded() (bool, error) { m.ctrl.T.Helper() @@ -186,18 +621,224 @@ func (mr *MockHostManagerInterfaceMockRecorder) RdmaIsLoaded() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RdmaIsLoaded", reflect.TypeOf((*MockHostManagerInterface)(nil).RdmaIsLoaded)) } +// ReadScriptManifestFile mocks base method. +func (m *MockHostManagerInterface) ReadScriptManifestFile(path string) (*types.ScriptManifestFile, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadScriptManifestFile", path) + ret0, _ := ret[0].(*types.ScriptManifestFile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadScriptManifestFile indicates an expected call of ReadScriptManifestFile. +func (mr *MockHostManagerInterfaceMockRecorder) ReadScriptManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadScriptManifestFile", reflect.TypeOf((*MockHostManagerInterface)(nil).ReadScriptManifestFile), path) +} + +// ReadService mocks base method. +func (m *MockHostManagerInterface) ReadService(servicePath string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadService", servicePath) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadService indicates an expected call of ReadService. +func (mr *MockHostManagerInterfaceMockRecorder) ReadService(servicePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadService", reflect.TypeOf((*MockHostManagerInterface)(nil).ReadService), servicePath) +} + +// ReadServiceInjectionManifestFile mocks base method. +func (m *MockHostManagerInterface) ReadServiceInjectionManifestFile(path string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadServiceInjectionManifestFile", path) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadServiceInjectionManifestFile indicates an expected call of ReadServiceInjectionManifestFile. +func (mr *MockHostManagerInterfaceMockRecorder) ReadServiceInjectionManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadServiceInjectionManifestFile", reflect.TypeOf((*MockHostManagerInterface)(nil).ReadServiceInjectionManifestFile), path) +} + +// ReadServiceManifestFile mocks base method. +func (m *MockHostManagerInterface) ReadServiceManifestFile(path string) (*types.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadServiceManifestFile", path) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadServiceManifestFile indicates an expected call of ReadServiceManifestFile. +func (mr *MockHostManagerInterfaceMockRecorder) ReadServiceManifestFile(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadServiceManifestFile", reflect.TypeOf((*MockHostManagerInterface)(nil).ReadServiceManifestFile), path) +} + +// RebindVfToDefaultDriver mocks base method. +func (m *MockHostManagerInterface) RebindVfToDefaultDriver(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RebindVfToDefaultDriver", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// RebindVfToDefaultDriver indicates an expected call of RebindVfToDefaultDriver. +func (mr *MockHostManagerInterfaceMockRecorder) RebindVfToDefaultDriver(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RebindVfToDefaultDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).RebindVfToDefaultDriver), pciAddr) +} + // ReloadDriver mocks base method. -func (m *MockHostManagerInterface) ReloadDriver(arg0 string) error { +func (m *MockHostManagerInterface) ReloadDriver(driver string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReloadDriver", arg0) + ret := m.ctrl.Call(m, "ReloadDriver", driver) ret0, _ := ret[0].(error) return ret0 } // ReloadDriver indicates an expected call of ReloadDriver. -func (mr *MockHostManagerInterfaceMockRecorder) ReloadDriver(arg0 interface{}) *gomock.Call { +func (mr *MockHostManagerInterfaceMockRecorder) ReloadDriver(driver interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).ReloadDriver), driver) +} + +// RemoveFromService mocks base method. +func (m *MockHostManagerInterface) RemoveFromService(service *types.Service, options ...*unit.UnitOption) (*types.Service, error) { + m.ctrl.T.Helper() + varargs := []interface{}{service} + for _, a := range options { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RemoveFromService", varargs...) + ret0, _ := ret[0].(*types.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RemoveFromService indicates an expected call of RemoveFromService. +func (mr *MockHostManagerInterfaceMockRecorder) RemoveFromService(service interface{}, options ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{service}, options...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFromService", reflect.TypeOf((*MockHostManagerInterface)(nil).RemoveFromService), varargs...) +} + +// RemoveUdevRule mocks base method. +func (m *MockHostManagerInterface) RemoveUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveUdevRule indicates an expected call of RemoveUdevRule. +func (mr *MockHostManagerInterfaceMockRecorder) RemoveUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveUdevRule", reflect.TypeOf((*MockHostManagerInterface)(nil).RemoveUdevRule), pfPciAddress) +} + +// RemoveVfRepresentorUdevRule mocks base method. +func (m *MockHostManagerInterface) RemoveVfRepresentorUdevRule(pfPciAddress string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveVfRepresentorUdevRule", pfPciAddress) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveVfRepresentorUdevRule indicates an expected call of RemoveVfRepresentorUdevRule. +func (mr *MockHostManagerInterfaceMockRecorder) RemoveVfRepresentorUdevRule(pfPciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVfRepresentorUdevRule", reflect.TypeOf((*MockHostManagerInterface)(nil).RemoveVfRepresentorUdevRule), pfPciAddress) +} + +// ResetSriovDevice mocks base method. +func (m *MockHostManagerInterface) ResetSriovDevice(ifaceStatus v1.InterfaceExt) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetSriovDevice", ifaceStatus) + ret0, _ := ret[0].(error) + return ret0 +} + +// ResetSriovDevice indicates an expected call of ResetSriovDevice. +func (mr *MockHostManagerInterfaceMockRecorder) ResetSriovDevice(ifaceStatus interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetSriovDevice", reflect.TypeOf((*MockHostManagerInterface)(nil).ResetSriovDevice), ifaceStatus) +} + +// SetNetdevMTU mocks base method. +func (m *MockHostManagerInterface) SetNetdevMTU(pciAddr string, mtu int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetNetdevMTU", pciAddr, mtu) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetNetdevMTU indicates an expected call of SetNetdevMTU. +func (mr *MockHostManagerInterfaceMockRecorder) SetNetdevMTU(pciAddr, mtu interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadDriver", reflect.TypeOf((*MockHostManagerInterface)(nil).ReloadDriver), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetdevMTU", reflect.TypeOf((*MockHostManagerInterface)(nil).SetNetdevMTU), pciAddr, mtu) +} + +// SetNicSriovMode mocks base method. +func (m *MockHostManagerInterface) SetNicSriovMode(pciAddr, mode string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetNicSriovMode", pciAddr, mode) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetNicSriovMode indicates an expected call of SetNicSriovMode. +func (mr *MockHostManagerInterfaceMockRecorder) SetNicSriovMode(pciAddr, mode interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNicSriovMode", reflect.TypeOf((*MockHostManagerInterface)(nil).SetNicSriovMode), pciAddr, mode) +} + +// SetSriovNumVfs mocks base method. +func (m *MockHostManagerInterface) SetSriovNumVfs(pciAddr string, numVfs int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetSriovNumVfs", pciAddr, numVfs) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetSriovNumVfs indicates an expected call of SetSriovNumVfs. +func (mr *MockHostManagerInterfaceMockRecorder) SetSriovNumVfs(pciAddr, numVfs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSriovNumVfs", reflect.TypeOf((*MockHostManagerInterface)(nil).SetSriovNumVfs), pciAddr, numVfs) +} + +// SetVfAdminMac mocks base method. +func (m *MockHostManagerInterface) SetVfAdminMac(vfAddr string, pfLink, vfLink netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetVfAdminMac", vfAddr, pfLink, vfLink) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetVfAdminMac indicates an expected call of SetVfAdminMac. +func (mr *MockHostManagerInterfaceMockRecorder) SetVfAdminMac(vfAddr, pfLink, vfLink interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVfAdminMac", reflect.TypeOf((*MockHostManagerInterface)(nil).SetVfAdminMac), vfAddr, pfLink, vfLink) +} + +// SetVfGUID mocks base method. +func (m *MockHostManagerInterface) SetVfGUID(vfAddr string, pfLink netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetVfGUID", vfAddr, pfLink) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetVfGUID indicates an expected call of SetVfGUID. +func (mr *MockHostManagerInterfaceMockRecorder) SetVfGUID(vfAddr, pfLink interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVfGUID", reflect.TypeOf((*MockHostManagerInterface)(nil).SetVfGUID), vfAddr, pfLink) } // TriggerUdevEvent mocks base method. @@ -252,3 +893,117 @@ func (mr *MockHostManagerInterfaceMockRecorder) TryEnableVhostNet() *gomock.Call mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryEnableVhostNet", reflect.TypeOf((*MockHostManagerInterface)(nil).TryEnableVhostNet)) } + +// TryGetInterfaceName mocks base method. +func (m *MockHostManagerInterface) TryGetInterfaceName(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryGetInterfaceName", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// TryGetInterfaceName indicates an expected call of TryGetInterfaceName. +func (mr *MockHostManagerInterfaceMockRecorder) TryGetInterfaceName(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryGetInterfaceName", reflect.TypeOf((*MockHostManagerInterface)(nil).TryGetInterfaceName), pciAddr) +} + +// TryToGetVirtualInterfaceName mocks base method. +func (m *MockHostManagerInterface) TryToGetVirtualInterfaceName(pciAddr string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryToGetVirtualInterfaceName", pciAddr) + ret0, _ := ret[0].(string) + return ret0 +} + +// TryToGetVirtualInterfaceName indicates an expected call of TryToGetVirtualInterfaceName. +func (mr *MockHostManagerInterfaceMockRecorder) TryToGetVirtualInterfaceName(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryToGetVirtualInterfaceName", reflect.TypeOf((*MockHostManagerInterface)(nil).TryToGetVirtualInterfaceName), pciAddr) +} + +// Unbind mocks base method. +func (m *MockHostManagerInterface) Unbind(pciAddr string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Unbind", pciAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// Unbind indicates an expected call of Unbind. +func (mr *MockHostManagerInterfaceMockRecorder) Unbind(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unbind", reflect.TypeOf((*MockHostManagerInterface)(nil).Unbind), pciAddr) +} + +// UnbindDriverByBusAndDevice mocks base method. +func (m *MockHostManagerInterface) UnbindDriverByBusAndDevice(bus, device string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindDriverByBusAndDevice", bus, device) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindDriverByBusAndDevice indicates an expected call of UnbindDriverByBusAndDevice. +func (mr *MockHostManagerInterfaceMockRecorder) UnbindDriverByBusAndDevice(bus, device interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindDriverByBusAndDevice", reflect.TypeOf((*MockHostManagerInterface)(nil).UnbindDriverByBusAndDevice), bus, device) +} + +// UnbindDriverIfNeeded mocks base method. +func (m *MockHostManagerInterface) UnbindDriverIfNeeded(pciAddr string, isRdma bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindDriverIfNeeded", pciAddr, isRdma) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindDriverIfNeeded indicates an expected call of UnbindDriverIfNeeded. +func (mr *MockHostManagerInterfaceMockRecorder) UnbindDriverIfNeeded(pciAddr, isRdma interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindDriverIfNeeded", reflect.TypeOf((*MockHostManagerInterface)(nil).UnbindDriverIfNeeded), pciAddr, isRdma) +} + +// UpdateSystemService mocks base method. +func (m *MockHostManagerInterface) UpdateSystemService(serviceObj *types.Service) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateSystemService", serviceObj) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateSystemService indicates an expected call of UpdateSystemService. +func (mr *MockHostManagerInterfaceMockRecorder) UpdateSystemService(serviceObj interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSystemService", reflect.TypeOf((*MockHostManagerInterface)(nil).UpdateSystemService), serviceObj) +} + +// VFIsReady mocks base method. +func (m *MockHostManagerInterface) VFIsReady(pciAddr string) (netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VFIsReady", pciAddr) + ret0, _ := ret[0].(netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// VFIsReady indicates an expected call of VFIsReady. +func (mr *MockHostManagerInterfaceMockRecorder) VFIsReady(pciAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VFIsReady", reflect.TypeOf((*MockHostManagerInterface)(nil).VFIsReady), pciAddr) +} + +// WriteSwitchdevConfFile mocks base method. +func (m *MockHostManagerInterface) WriteSwitchdevConfFile(newState *v1.SriovNetworkNodeState, pfsToSkip map[string]bool) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteSwitchdevConfFile", newState, pfsToSkip) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WriteSwitchdevConfFile indicates an expected call of WriteSwitchdevConfFile. +func (mr *MockHostManagerInterfaceMockRecorder) WriteSwitchdevConfFile(newState, pfsToSkip interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSwitchdevConfFile", reflect.TypeOf((*MockHostManagerInterface)(nil).WriteSwitchdevConfFile), newState, pfsToSkip) +} diff --git a/pkg/host/store/mock/mock_store.go b/pkg/host/store/mock/mock_store.go new file mode 100644 index 000000000..2e0071dfd --- /dev/null +++ b/pkg/host/store/mock/mock_store.go @@ -0,0 +1,108 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: store.go + +// Package mock_store is a generated GoMock package. +package mock_store + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" +) + +// MockManagerInterface is a mock of ManagerInterface interface. +type MockManagerInterface struct { + ctrl *gomock.Controller + recorder *MockManagerInterfaceMockRecorder +} + +// MockManagerInterfaceMockRecorder is the mock recorder for MockManagerInterface. +type MockManagerInterfaceMockRecorder struct { + mock *MockManagerInterface +} + +// NewMockManagerInterface creates a new mock instance. +func NewMockManagerInterface(ctrl *gomock.Controller) *MockManagerInterface { + mock := &MockManagerInterface{ctrl: ctrl} + mock.recorder = &MockManagerInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManagerInterface) EXPECT() *MockManagerInterfaceMockRecorder { + return m.recorder +} + +// ClearPCIAddressFolder mocks base method. +func (m *MockManagerInterface) ClearPCIAddressFolder() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClearPCIAddressFolder") + ret0, _ := ret[0].(error) + return ret0 +} + +// ClearPCIAddressFolder indicates an expected call of ClearPCIAddressFolder. +func (mr *MockManagerInterfaceMockRecorder) ClearPCIAddressFolder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearPCIAddressFolder", reflect.TypeOf((*MockManagerInterface)(nil).ClearPCIAddressFolder)) +} + +// GetCheckPointNodeState mocks base method. +func (m *MockManagerInterface) GetCheckPointNodeState() (*v1.SriovNetworkNodeState, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCheckPointNodeState") + ret0, _ := ret[0].(*v1.SriovNetworkNodeState) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCheckPointNodeState indicates an expected call of GetCheckPointNodeState. +func (mr *MockManagerInterfaceMockRecorder) GetCheckPointNodeState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCheckPointNodeState", reflect.TypeOf((*MockManagerInterface)(nil).GetCheckPointNodeState)) +} + +// LoadPfsStatus mocks base method. +func (m *MockManagerInterface) LoadPfsStatus(pciAddress string) (*v1.Interface, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadPfsStatus", pciAddress) + ret0, _ := ret[0].(*v1.Interface) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// LoadPfsStatus indicates an expected call of LoadPfsStatus. +func (mr *MockManagerInterfaceMockRecorder) LoadPfsStatus(pciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPfsStatus", reflect.TypeOf((*MockManagerInterface)(nil).LoadPfsStatus), pciAddress) +} + +// SaveLastPfAppliedStatus mocks base method. +func (m *MockManagerInterface) SaveLastPfAppliedStatus(PfInfo *v1.Interface) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveLastPfAppliedStatus", PfInfo) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveLastPfAppliedStatus indicates an expected call of SaveLastPfAppliedStatus. +func (mr *MockManagerInterfaceMockRecorder) SaveLastPfAppliedStatus(PfInfo interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveLastPfAppliedStatus", reflect.TypeOf((*MockManagerInterface)(nil).SaveLastPfAppliedStatus), PfInfo) +} + +// WriteCheckpointFile mocks base method. +func (m *MockManagerInterface) WriteCheckpointFile(arg0 *v1.SriovNetworkNodeState) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteCheckpointFile", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteCheckpointFile indicates an expected call of WriteCheckpointFile. +func (mr *MockManagerInterfaceMockRecorder) WriteCheckpointFile(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteCheckpointFile", reflect.TypeOf((*MockManagerInterface)(nil).WriteCheckpointFile), arg0) +} diff --git a/pkg/utils/store.go b/pkg/host/store/store.go similarity index 50% rename from pkg/utils/store.go rename to pkg/host/store/store.go index dd1b44626..77e4bbd32 100644 --- a/pkg/utils/store.go +++ b/pkg/host/store/store.go @@ -1,50 +1,48 @@ -package utils +package store import ( "encoding/json" "fmt" "os" - "path" "path/filepath" "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" -) - -const ( - SriovConfBasePath = "/etc/sriov-operator" - PfAppliedConfig = SriovConfBasePath + "/pci" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // Contains all the file storing on the host // -//go:generate ../../bin/mockgen -destination mock/mock_store.go -source store.go -type StoreManagerInterface interface { +//go:generate ../../../bin/mockgen -destination mock/mock_store.go -source store.go +type ManagerInterface interface { ClearPCIAddressFolder() error SaveLastPfAppliedStatus(PfInfo *sriovnetworkv1.Interface) error LoadPfsStatus(pciAddress string) (*sriovnetworkv1.Interface, bool, error) -} -type StoreManager struct { - RunOnHost bool + GetCheckPointNodeState() (*sriovnetworkv1.SriovNetworkNodeState, error) + WriteCheckpointFile(*sriovnetworkv1.SriovNetworkNodeState) error } -// NewStoreManager: create the initial folders needed to store the info about the PF -// and return a storeManager struct that implements the StoreManagerInterface interface -func NewStoreManager(runOnHost bool) (StoreManagerInterface, error) { - if err := createOperatorConfigFolderIfNeeded(runOnHost); err != nil { +type manager struct{} + +// NewManager: create the initial folders needed to store the info about the PF +// and return a manager struct that implements the ManagerInterface interface +func NewManager() (ManagerInterface, error) { + if err := createOperatorConfigFolderIfNeeded(); err != nil { return nil, err } - return &StoreManager{runOnHost}, nil + return &manager{}, nil } // createOperatorConfigFolderIfNeeded: create the operator base folder on the host // together with the pci folder to save the PF status objects as json files -func createOperatorConfigFolderIfNeeded(runOnHost bool) error { - hostExtension := getHostExtension(runOnHost) - SriovConfBasePathUse := filepath.Join(hostExtension, SriovConfBasePath) +func createOperatorConfigFolderIfNeeded() error { + hostExtension := utils.GetHostExtension() + SriovConfBasePathUse := filepath.Join(hostExtension, consts.SriovConfBasePath) _, err := os.Stat(SriovConfBasePathUse) if err != nil { if os.IsNotExist(err) { @@ -57,7 +55,7 @@ func createOperatorConfigFolderIfNeeded(runOnHost bool) error { } } - PfAppliedConfigUse := filepath.Join(hostExtension, PfAppliedConfig) + PfAppliedConfigUse := filepath.Join(hostExtension, consts.PfAppliedConfig) _, err = os.Stat(PfAppliedConfigUse) if err != nil { if os.IsNotExist(err) { @@ -74,9 +72,9 @@ func createOperatorConfigFolderIfNeeded(runOnHost bool) error { } // ClearPCIAddressFolder: removes all the PFs storage information -func (s *StoreManager) ClearPCIAddressFolder() error { - hostExtension := getHostExtension(s.RunOnHost) - PfAppliedConfigUse := filepath.Join(hostExtension, PfAppliedConfig) +func (s *manager) ClearPCIAddressFolder() error { + hostExtension := utils.GetHostExtension() + PfAppliedConfigUse := filepath.Join(hostExtension, consts.PfAppliedConfig) _, err := os.Stat(PfAppliedConfigUse) if err != nil { if os.IsNotExist(err) { @@ -100,24 +98,24 @@ func (s *StoreManager) ClearPCIAddressFolder() error { // SaveLastPfAppliedStatus will save the PF object as a json into the /etc/sriov-operator/pci/ // this function must be called after running the chroot function -func (s *StoreManager) SaveLastPfAppliedStatus(PfInfo *sriovnetworkv1.Interface) error { +func (s *manager) SaveLastPfAppliedStatus(PfInfo *sriovnetworkv1.Interface) error { data, err := json.Marshal(PfInfo) if err != nil { log.Log.Error(err, "failed to marshal PF status", "status", *PfInfo) return err } - hostExtension := getHostExtension(s.RunOnHost) - pathFile := filepath.Join(hostExtension, PfAppliedConfig, PfInfo.PciAddress) + hostExtension := utils.GetHostExtension() + pathFile := filepath.Join(hostExtension, consts.PfAppliedConfig, PfInfo.PciAddress) err = os.WriteFile(pathFile, data, 0644) return err } // LoadPfsStatus convert the /etc/sriov-operator/pci/ json to pfstatus // returns false if the file doesn't exist. -func (s *StoreManager) LoadPfsStatus(pciAddress string) (*sriovnetworkv1.Interface, bool, error) { - hostExtension := getHostExtension(s.RunOnHost) - pathFile := filepath.Join(hostExtension, PfAppliedConfig, pciAddress) +func (s *manager) LoadPfsStatus(pciAddress string) (*sriovnetworkv1.Interface, bool, error) { + hostExtension := utils.GetHostExtension() + pathFile := filepath.Join(hostExtension, consts.PfAppliedConfig, pciAddress) pfStatus := &sriovnetworkv1.Interface{} data, err := os.ReadFile(pathFile) if err != nil { @@ -137,9 +135,45 @@ func (s *StoreManager) LoadPfsStatus(pciAddress string) (*sriovnetworkv1.Interfa return pfStatus, true, nil } -func getHostExtension(runOnHost bool) string { - if !runOnHost { - return path.Join(FilesystemRoot, "/host") +func (s *manager) GetCheckPointNodeState() (*sriovnetworkv1.SriovNetworkNodeState, error) { + log.Log.Info("getCheckPointNodeState()") + configdir := filepath.Join(vars.Destdir, consts.CheckpointFileName) + file, err := os.OpenFile(configdir, os.O_RDONLY, 0644) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err } - return FilesystemRoot + defer file.Close() + if err = json.NewDecoder(file).Decode(&sriovnetworkv1.InitialState); err != nil { + return nil, err + } + + return &sriovnetworkv1.InitialState, nil +} + +func (s *manager) WriteCheckpointFile(ns *sriovnetworkv1.SriovNetworkNodeState) error { + configdir := filepath.Join(vars.Destdir, consts.CheckpointFileName) + file, err := os.OpenFile(configdir, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + return err + } + defer file.Close() + log.Log.Info("WriteCheckpointFile(): try to decode the checkpoint file") + if err = json.NewDecoder(file).Decode(&sriovnetworkv1.InitialState); err != nil { + log.Log.V(2).Error(err, "WriteCheckpointFile(): fail to decode, writing new file instead") + log.Log.Info("WriteCheckpointFile(): write checkpoint file") + if err = file.Truncate(0); err != nil { + return err + } + if _, err = file.Seek(0, 0); err != nil { + return err + } + if err = json.NewEncoder(file).Encode(*ns); err != nil { + return err + } + sriovnetworkv1.InitialState = *ns + } + return nil } diff --git a/pkg/host/types/interfaces.go b/pkg/host/types/interfaces.go new file mode 100644 index 000000000..6a12f29cd --- /dev/null +++ b/pkg/host/types/interfaces.go @@ -0,0 +1,178 @@ +package types + +import ( + "github.com/coreos/go-systemd/v22/unit" + "github.com/jaypipes/ghw" + "github.com/vishvananda/netlink" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store" +) + +type KernelInterface interface { + // TryEnableTun load the tun kernel module + TryEnableTun() + // TryEnableVhostNet load the vhost-net kernel module + TryEnableVhostNet() + // TryEnableRdma tries to enable RDMA on the machine base on the operating system + // if the package doesn't exist it will also will try to install it + // supported operating systems are RHEL RHCOS and ubuntu + TryEnableRdma() (bool, error) + // TriggerUdevEvent triggers a udev event + TriggerUdevEvent() error + // GetCurrentKernelArgs reads the /proc/cmdline to check the current kernel arguments + GetCurrentKernelArgs() (string, error) + // IsKernelArgsSet check is the requested kernel arguments are set + IsKernelArgsSet(cmdLine, karg string) bool + // Unbind unbinds a virtual function from is current driver + Unbind(pciAddr string) error + // BindDpdkDriver binds the virtual function to a DPDK driver + BindDpdkDriver(pciAddr, driver string) error + // BindDefaultDriver binds the virtual function to is default driver + BindDefaultDriver(pciAddr string) error + // BindDriverByBusAndDevice binds device to the provided driver + // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" + // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA + // driver - the name of the driver, e.g. vfio-pci or vhost_vdpa. + BindDriverByBusAndDevice(bus, device, driver string) error + // HasDriver returns try if the virtual function is bind to a driver + HasDriver(pciAddr string) (bool, string) + // RebindVfToDefaultDriver rebinds the virtual function to is default driver + RebindVfToDefaultDriver(pciAddr string) error + // UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver + // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" + // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA + UnbindDriverByBusAndDevice(bus, device string) error + // UnbindDriverIfNeeded unbinds the virtual function from a driver if needed + UnbindDriverIfNeeded(pciAddr string, isRdma bool) error + // LoadKernelModule loads a kernel module to the host + LoadKernelModule(name string, args ...string) error + // IsKernelModuleLoaded returns try if the requested kernel module is loaded + IsKernelModuleLoaded(name string) (bool, error) + // ReloadDriver reloads a requested driver + ReloadDriver(driver string) error + // IsKernelLockdownMode returns true if the kernel is in lockdown mode + IsKernelLockdownMode() bool + // IsRHELSystem returns try if the system is a RHEL base + IsRHELSystem() (bool, error) + // IsUbuntuSystem returns try if the system is an ubuntu base + IsUbuntuSystem() (bool, error) + // IsCoreOS returns true if the system is a CoreOS or RHCOS base + IsCoreOS() (bool, error) + // RdmaIsLoaded returns try if RDMA kernel modules are loaded + RdmaIsLoaded() (bool, error) + // EnableRDMA enable RDMA on the system + EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) + // InstallRDMA install RDMA packages on the system + InstallRDMA(packageManager string) error + // EnableRDMAOnRHELMachine enable RDMA on a RHEL base system + EnableRDMAOnRHELMachine() (bool, error) + // GetOSPrettyName returns OS name + GetOSPrettyName() (string, error) +} + +type NetworkInterface interface { + // TryToGetVirtualInterfaceName tries to find the virtio interface name base on pci address + // used for virtual environment where we pass SR-IOV virtual function into the system + // supported platform openstack + TryToGetVirtualInterfaceName(pciAddr string) string + // TryGetInterfaceName tries to find the SR-IOV virtual interface name base on pci address + TryGetInterfaceName(pciAddr string) string + // GetPhysSwitchID returns the physical switch ID for a specific pci address + GetPhysSwitchID(name string) (string, error) + // GetPhysPortName returns the physical port name for a specific pci address + GetPhysPortName(name string) (string, error) + // IsSwitchdev returns true of the pci address is on switchdev mode + IsSwitchdev(name string) bool + // GetNetdevMTU returns the interface MTU for devices attached to kernel drivers + GetNetdevMTU(pciAddr string) int + // SetNetdevMTU sets the MTU for a request interface + SetNetdevMTU(pciAddr string, mtu int) error + // GetNetDevMac returns the network interface mac address + GetNetDevMac(name string) string + // GetNetDevLinkSpeed returns the network interface link speed + GetNetDevLinkSpeed(name string) string +} + +type ServiceInterface interface { + // IsServiceExist checks if the requested systemd service exist on the system + IsServiceExist(servicePath string) (bool, error) + // IsServiceEnabled checks if the requested systemd service is enabled on the system + IsServiceEnabled(servicePath string) (bool, error) + // ReadService reads a systemd servers and return it as a struct + ReadService(servicePath string) (*Service, error) + // EnableService enables a systemd server on the host + EnableService(service *Service) error + // ReadServiceManifestFile reads the systemd manifest for a specific service + ReadServiceManifestFile(path string) (*Service, error) + // RemoveFromService removes a systemd service from the host + RemoveFromService(service *Service, options ...*unit.UnitOption) (*Service, error) + // ReadScriptManifestFile reads the script manifest from a systemd service + ReadScriptManifestFile(path string) (*ScriptManifestFile, error) + // ReadServiceInjectionManifestFile reads the injection manifest file for the systemd service + ReadServiceInjectionManifestFile(path string) (*Service, error) + // CompareServices compare two servers and return true if they are equal + CompareServices(serviceA, serviceB *Service) (bool, error) + // UpdateSystemService updates a system service on the host + UpdateSystemService(serviceObj *Service) error +} + +type SriovInterface interface { + // SetSriovNumVfs changes the number of virtual functions allocated for a specific + // physical function base on pci address + SetSriovNumVfs(pciAddr string, numVfs int) error + // GetVfInfo returns the virtual function information is the operator struct from the host information + GetVfInfo(pciAddr string, devices []*ghw.PCIDevice) sriovnetworkv1.VirtualFunction + // SetVfGUID sets the GUID for a virtual function + SetVfGUID(vfAddr string, pfLink netlink.Link) error + // VFIsReady returns the interface virtual function if the device is ready + VFIsReady(pciAddr string) (netlink.Link, error) + // SetVfAdminMac sets the virtual function administrative mac address via the physical function + SetVfAdminMac(vfAddr string, pfLink netlink.Link, vfLink netlink.Link) error + // GetNicSriovMode returns the interface mode + // supported modes SR-IOV legacy and switchdev + GetNicSriovMode(pciAddr string) (string, error) + // SetNicSriovMode configure the interface mode + // supported modes SR-IOV legacy and switchdev + SetNicSriovMode(pciAddr, mode string) error + // GetLinkType return the link type + // supported types are ethernet and infiniband + GetLinkType(ifaceStatus sriovnetworkv1.InterfaceExt) string + // ResetSriovDevice resets the number of virtual function for the specific physical function to zero + ResetSriovDevice(ifaceStatus sriovnetworkv1.InterfaceExt) error + // DiscoverSriovDevices returns a list of all the available SR-IOV capable network interfaces on the system + DiscoverSriovDevices(storeManager store.ManagerInterface) ([]sriovnetworkv1.InterfaceExt, error) + // ConfigSriovDevice configure the request SR-IOV device with the desired configuration + ConfigSriovDevice(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) error + // ConfigSriovInterfaces configure multiple SR-IOV devices with the desired configuration + ConfigSriovInterfaces(storeManager store.ManagerInterface, interfaces []sriovnetworkv1.Interface, + ifaceStatuses []sriovnetworkv1.InterfaceExt, pfsToConfig map[string]bool) error + // ConfigSriovInterfaces configure virtual functions for virtual environments with the desired configuration + ConfigSriovDeviceVirtual(iface *sriovnetworkv1.Interface) error +} + +type UdevInterface interface { + // WriteSwitchdevConfFile writes the needed switchdev configuration files for HW offload support + WriteSwitchdevConfFile(newState *sriovnetworkv1.SriovNetworkNodeState, pfsToSkip map[string]bool) (bool, error) + // PrepareNMUdevRule creates the needed udev rules to disable NetworkManager from + // our managed SR-IOV virtual functions + PrepareNMUdevRule(supportedVfIds []string) error + // AddUdevRule adds a udev rule that disables network-manager for VFs on the concrete PF + AddUdevRule(pfPciAddress string) error + // RemoveUdevRule removes a udev rule that disables network-manager for VFs on the concrete PF + RemoveUdevRule(pfPciAddress string) error + // AddVfRepresentorUdevRule adds udev rule that renames VF representors on the concrete PF + AddVfRepresentorUdevRule(pfPciAddress, pfName, pfSwitchID, pfSwitchPort string) error + // RemoveVfRepresentorUdevRule removes udev rule that renames VF representors on the concrete PF + RemoveVfRepresentorUdevRule(pfPciAddress string) error +} + +type VdpaInterface interface { + // CreateVDPADevice creates VDPA device for VF with required type + CreateVDPADevice(pciAddr, vdpaType string) error + // DeleteVDPADevice removes VDPA device for provided pci address + DeleteVDPADevice(pciAddr string) error + // DiscoverVDPAType returns type of existing VDPA device for VF, + // returns empty string if VDPA device not found or unknown driver is in use + DiscoverVDPAType(pciAddr string) string +} diff --git a/pkg/service/types.go b/pkg/host/types/types.go similarity index 53% rename from pkg/service/types.go rename to pkg/host/types/types.go index aaa879a4d..935a34bfd 100644 --- a/pkg/service/types.go +++ b/pkg/host/types/types.go @@ -1,4 +1,15 @@ -package service +package types + +import ( + "github.com/coreos/go-systemd/v22/unit" +) + +// Service contains info about systemd service +type Service struct { + Name string + Path string + Content string +} // ServiceInjectionManifestFile service injection manifest file structure type ServiceInjectionManifestFile struct { @@ -21,3 +32,12 @@ type ScriptManifestFile struct { Inline string } } + +var ( + // Remove run condition form the service + ConditionOpt = &unit.UnitOption{ + Section: "Unit", + Name: "ConditionPathExists", + Value: "!/etc/ignition-machine-config-encapsulated.json", + } +) diff --git a/pkg/platforms/mock/mock_platforms.go b/pkg/platforms/mock/mock_platforms.go new file mode 100644 index 000000000..40eb5dc4c --- /dev/null +++ b/pkg/platforms/mock/mock_platforms.go @@ -0,0 +1,134 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: platforms.go + +// Package mock_platforms is a generated GoMock package. +package mock_platforms + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + openshift "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift" + versioned "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned" +) + +// MockInterface is a mock of Interface interface. +type MockInterface struct { + ctrl *gomock.Controller + recorder *MockInterfaceMockRecorder +} + +// MockInterfaceMockRecorder is the mock recorder for MockInterface. +type MockInterfaceMockRecorder struct { + mock *MockInterface +} + +// NewMockInterface creates a new mock instance. +func NewMockInterface(ctrl *gomock.Controller) *MockInterface { + mock := &MockInterface{ctrl: ctrl} + mock.recorder = &MockInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder +} + +// CreateOpenstackDevicesInfo mocks base method. +func (m *MockInterface) CreateOpenstackDevicesInfo() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateOpenstackDevicesInfo") + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateOpenstackDevicesInfo indicates an expected call of CreateOpenstackDevicesInfo. +func (mr *MockInterfaceMockRecorder) CreateOpenstackDevicesInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOpenstackDevicesInfo", reflect.TypeOf((*MockInterface)(nil).CreateOpenstackDevicesInfo)) +} + +// CreateOpenstackDevicesInfoFromNodeStatus mocks base method. +func (m *MockInterface) CreateOpenstackDevicesInfoFromNodeStatus(arg0 *v1.SriovNetworkNodeState) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CreateOpenstackDevicesInfoFromNodeStatus", arg0) +} + +// CreateOpenstackDevicesInfoFromNodeStatus indicates an expected call of CreateOpenstackDevicesInfoFromNodeStatus. +func (mr *MockInterfaceMockRecorder) CreateOpenstackDevicesInfoFromNodeStatus(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOpenstackDevicesInfoFromNodeStatus", reflect.TypeOf((*MockInterface)(nil).CreateOpenstackDevicesInfoFromNodeStatus), arg0) +} + +// DiscoverSriovDevicesVirtual mocks base method. +func (m *MockInterface) DiscoverSriovDevicesVirtual() ([]v1.InterfaceExt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverSriovDevicesVirtual") + ret0, _ := ret[0].([]v1.InterfaceExt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DiscoverSriovDevicesVirtual indicates an expected call of DiscoverSriovDevicesVirtual. +func (mr *MockInterfaceMockRecorder) DiscoverSriovDevicesVirtual() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverSriovDevicesVirtual", reflect.TypeOf((*MockInterface)(nil).DiscoverSriovDevicesVirtual)) +} + +// GetFlavor mocks base method. +func (m *MockInterface) GetFlavor() openshift.OpenshiftFlavor { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFlavor") + ret0, _ := ret[0].(openshift.OpenshiftFlavor) + return ret0 +} + +// GetFlavor indicates an expected call of GetFlavor. +func (mr *MockInterfaceMockRecorder) GetFlavor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFlavor", reflect.TypeOf((*MockInterface)(nil).GetFlavor)) +} + +// GetMcClient mocks base method. +func (m *MockInterface) GetMcClient() versioned.Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMcClient") + ret0, _ := ret[0].(versioned.Interface) + return ret0 +} + +// GetMcClient indicates an expected call of GetMcClient. +func (mr *MockInterfaceMockRecorder) GetMcClient() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMcClient", reflect.TypeOf((*MockInterface)(nil).GetMcClient)) +} + +// IsHypershift mocks base method. +func (m *MockInterface) IsHypershift() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsHypershift") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsHypershift indicates an expected call of IsHypershift. +func (mr *MockInterfaceMockRecorder) IsHypershift() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsHypershift", reflect.TypeOf((*MockInterface)(nil).IsHypershift)) +} + +// IsOpenshiftCluster mocks base method. +func (m *MockInterface) IsOpenshiftCluster() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsOpenshiftCluster") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsOpenshiftCluster indicates an expected call of IsOpenshiftCluster. +func (mr *MockInterfaceMockRecorder) IsOpenshiftCluster() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOpenshiftCluster", reflect.TypeOf((*MockInterface)(nil).IsOpenshiftCluster)) +} diff --git a/pkg/platforms/openshift/mock/mock_openshift.go b/pkg/platforms/openshift/mock/mock_openshift.go new file mode 100644 index 000000000..fb307a36e --- /dev/null +++ b/pkg/platforms/openshift/mock/mock_openshift.go @@ -0,0 +1,92 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: openshift.go + +// Package mock_openshift is a generated GoMock package. +package mock_openshift + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + openshift "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift" + versioned "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned" +) + +// MockOpenshiftContextInterface is a mock of OpenshiftContextInterface interface. +type MockOpenshiftContextInterface struct { + ctrl *gomock.Controller + recorder *MockOpenshiftContextInterfaceMockRecorder +} + +// MockOpenshiftContextInterfaceMockRecorder is the mock recorder for MockOpenshiftContextInterface. +type MockOpenshiftContextInterfaceMockRecorder struct { + mock *MockOpenshiftContextInterface +} + +// NewMockOpenshiftContextInterface creates a new mock instance. +func NewMockOpenshiftContextInterface(ctrl *gomock.Controller) *MockOpenshiftContextInterface { + mock := &MockOpenshiftContextInterface{ctrl: ctrl} + mock.recorder = &MockOpenshiftContextInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOpenshiftContextInterface) EXPECT() *MockOpenshiftContextInterfaceMockRecorder { + return m.recorder +} + +// GetFlavor mocks base method. +func (m *MockOpenshiftContextInterface) GetFlavor() openshift.OpenshiftFlavor { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFlavor") + ret0, _ := ret[0].(openshift.OpenshiftFlavor) + return ret0 +} + +// GetFlavor indicates an expected call of GetFlavor. +func (mr *MockOpenshiftContextInterfaceMockRecorder) GetFlavor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFlavor", reflect.TypeOf((*MockOpenshiftContextInterface)(nil).GetFlavor)) +} + +// GetMcClient mocks base method. +func (m *MockOpenshiftContextInterface) GetMcClient() versioned.Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMcClient") + ret0, _ := ret[0].(versioned.Interface) + return ret0 +} + +// GetMcClient indicates an expected call of GetMcClient. +func (mr *MockOpenshiftContextInterfaceMockRecorder) GetMcClient() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMcClient", reflect.TypeOf((*MockOpenshiftContextInterface)(nil).GetMcClient)) +} + +// IsHypershift mocks base method. +func (m *MockOpenshiftContextInterface) IsHypershift() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsHypershift") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsHypershift indicates an expected call of IsHypershift. +func (mr *MockOpenshiftContextInterfaceMockRecorder) IsHypershift() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsHypershift", reflect.TypeOf((*MockOpenshiftContextInterface)(nil).IsHypershift)) +} + +// IsOpenshiftCluster mocks base method. +func (m *MockOpenshiftContextInterface) IsOpenshiftCluster() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsOpenshiftCluster") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsOpenshiftCluster indicates an expected call of IsOpenshiftCluster. +func (mr *MockOpenshiftContextInterfaceMockRecorder) IsOpenshiftCluster() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOpenshiftCluster", reflect.TypeOf((*MockOpenshiftContextInterface)(nil).IsOpenshiftCluster)) +} diff --git a/pkg/utils/openshift_context.go b/pkg/platforms/openshift/openshift.go similarity index 50% rename from pkg/utils/openshift_context.go rename to pkg/platforms/openshift/openshift.go index 44a5b5e41..7ba2b6e29 100644 --- a/pkg/utils/openshift_context.go +++ b/pkg/platforms/openshift/openshift.go @@ -1,10 +1,12 @@ -package utils +package openshift import ( mcclientset "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) // OpenshiftFlavor holds metadata about the type of Openshift environment the operator is in. @@ -17,8 +19,16 @@ const ( OpenshiftFlavorDefault OpenshiftFlavor = "default" ) -// OpenshiftContext contains metadata and structs utilized to interact with Openshift clusters -type OpenshiftContext struct { +//go:generate ../../../bin/mockgen -destination mock/mock_openshift.go -source openshift.go +type OpenshiftContextInterface interface { + GetFlavor() OpenshiftFlavor + GetMcClient() mcclientset.Interface + IsOpenshiftCluster() bool + IsHypershift() bool +} + +// openshiftContext contains metadata and structs utilized to interact with Openshift clusters +type openshiftContext struct { // McClient is a client for MachineConfigs in an Openshift environment McClient mcclientset.Interface @@ -29,25 +39,25 @@ type OpenshiftContext struct { OpenshiftFlavor OpenshiftFlavor } -func NewOpenshiftContext(config *rest.Config, scheme *runtime.Scheme) (*OpenshiftContext, error) { - if ClusterType != ClusterTypeOpenshift { - return &OpenshiftContext{nil, false, ""}, nil +func New() (OpenshiftContextInterface, error) { + if vars.ClusterType != consts.ClusterTypeOpenshift { + return &openshiftContext{nil, false, ""}, nil } - mcclient, err := mcclientset.NewForConfig(config) + mcclient, err := mcclientset.NewForConfig(vars.Config) if err != nil { return nil, err } openshiftFlavor := OpenshiftFlavorDefault - infraClient, err := client.New(config, client.Options{ - Scheme: scheme, + infraClient, err := client.New(vars.Config, client.Options{ + Scheme: vars.Scheme, }) if err != nil { return nil, err } - isHypershift, err := IsExternalControlPlaneCluster(infraClient) + isHypershift, err := utils.IsExternalControlPlaneCluster(infraClient) if err != nil { return nil, err } @@ -56,13 +66,21 @@ func NewOpenshiftContext(config *rest.Config, scheme *runtime.Scheme) (*Openshif openshiftFlavor = OpenshiftFlavorHypershift } - return &OpenshiftContext{mcclient, true, openshiftFlavor}, nil + return &openshiftContext{mcclient, true, openshiftFlavor}, nil +} + +func (c *openshiftContext) GetFlavor() OpenshiftFlavor { + return c.OpenshiftFlavor +} + +func (c *openshiftContext) GetMcClient() mcclientset.Interface { + return c.McClient } -func (c OpenshiftContext) IsOpenshiftCluster() bool { +func (c openshiftContext) IsOpenshiftCluster() bool { return c.IsOpenShiftCluster } -func (c OpenshiftContext) IsHypershift() bool { +func (c openshiftContext) IsHypershift() bool { return c.OpenshiftFlavor == OpenshiftFlavorHypershift } diff --git a/pkg/platforms/openstack/mock/mock_openstack.go b/pkg/platforms/openstack/mock/mock_openstack.go new file mode 100644 index 000000000..9ef989297 --- /dev/null +++ b/pkg/platforms/openstack/mock/mock_openstack.go @@ -0,0 +1,76 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: openstack.go + +// Package mock_openstack is a generated GoMock package. +package mock_openstack + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" +) + +// MockOpenstackInterface is a mock of OpenstackInterface interface. +type MockOpenstackInterface struct { + ctrl *gomock.Controller + recorder *MockOpenstackInterfaceMockRecorder +} + +// MockOpenstackInterfaceMockRecorder is the mock recorder for MockOpenstackInterface. +type MockOpenstackInterfaceMockRecorder struct { + mock *MockOpenstackInterface +} + +// NewMockOpenstackInterface creates a new mock instance. +func NewMockOpenstackInterface(ctrl *gomock.Controller) *MockOpenstackInterface { + mock := &MockOpenstackInterface{ctrl: ctrl} + mock.recorder = &MockOpenstackInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOpenstackInterface) EXPECT() *MockOpenstackInterfaceMockRecorder { + return m.recorder +} + +// CreateOpenstackDevicesInfo mocks base method. +func (m *MockOpenstackInterface) CreateOpenstackDevicesInfo() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateOpenstackDevicesInfo") + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateOpenstackDevicesInfo indicates an expected call of CreateOpenstackDevicesInfo. +func (mr *MockOpenstackInterfaceMockRecorder) CreateOpenstackDevicesInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOpenstackDevicesInfo", reflect.TypeOf((*MockOpenstackInterface)(nil).CreateOpenstackDevicesInfo)) +} + +// CreateOpenstackDevicesInfoFromNodeStatus mocks base method. +func (m *MockOpenstackInterface) CreateOpenstackDevicesInfoFromNodeStatus(arg0 *v1.SriovNetworkNodeState) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CreateOpenstackDevicesInfoFromNodeStatus", arg0) +} + +// CreateOpenstackDevicesInfoFromNodeStatus indicates an expected call of CreateOpenstackDevicesInfoFromNodeStatus. +func (mr *MockOpenstackInterfaceMockRecorder) CreateOpenstackDevicesInfoFromNodeStatus(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOpenstackDevicesInfoFromNodeStatus", reflect.TypeOf((*MockOpenstackInterface)(nil).CreateOpenstackDevicesInfoFromNodeStatus), arg0) +} + +// DiscoverSriovDevicesVirtual mocks base method. +func (m *MockOpenstackInterface) DiscoverSriovDevicesVirtual() ([]v1.InterfaceExt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DiscoverSriovDevicesVirtual") + ret0, _ := ret[0].([]v1.InterfaceExt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DiscoverSriovDevicesVirtual indicates an expected call of DiscoverSriovDevicesVirtual. +func (mr *MockOpenstackInterfaceMockRecorder) DiscoverSriovDevicesVirtual() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoverSriovDevicesVirtual", reflect.TypeOf((*MockOpenstackInterface)(nil).DiscoverSriovDevicesVirtual)) +} diff --git a/pkg/utils/utils_virtual.go b/pkg/platforms/openstack/openstack.go similarity index 65% rename from pkg/utils/utils_virtual.go rename to pkg/platforms/openstack/openstack.go index a5e6ddc84..b86e079ca 100644 --- a/pkg/utils/utils_virtual.go +++ b/pkg/platforms/openstack/openstack.go @@ -1,12 +1,10 @@ -package utils +package openstack import ( "encoding/json" - "errors" "fmt" "io" "os" - "path/filepath" "strconv" "strings" @@ -18,52 +16,39 @@ import ( dputils "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" -) - -// PlatformType ... -type PlatformType int - -const ( - // Baremetal platform - Baremetal PlatformType = iota - // VirtualOpenStack ... - VirtualOpenStack -) - -func (e PlatformType) String() string { - switch e { - case Baremetal: - return "Baremetal" - case VirtualOpenStack: - return "Virtual/Openstack" - default: - return fmt.Sprintf("%d", int(e)) - } -} - -var ( - // PlatformMap contains supported platforms for virtual VF - PlatformMap = map[string]PlatformType{ - "openstack": VirtualOpenStack, - } + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" ) const ( ospHostMetaDataDir = "/host/var/config/openstack/2018-08-27" ospMetaDataDir = "/var/config/openstack/2018-08-27" ospMetaDataBaseURL = "http://169.254.169.254/openstack/2018-08-27" - ospHostNetworkDataFile = ospHostMetaDataDir + "/network_data.json" - ospHostMetaDataFile = ospHostMetaDataDir + "/meta_data.json" - ospNetworkDataURL = ospMetaDataBaseURL + "/network_data.json" - ospMetaDataURL = ospMetaDataBaseURL + "/meta_data.json" + ospNetworkDataJSON = "network_data.json" + ospMetaDataJSON = "meta_data.json" + ospHostNetworkDataFile = ospHostMetaDataDir + "/" + ospNetworkDataJSON + ospHostMetaDataFile = ospHostMetaDataDir + "/" + ospMetaDataJSON + ospNetworkDataURL = ospMetaDataBaseURL + "/" + ospNetworkDataJSON + ospMetaDataURL = ospMetaDataBaseURL + "/" + ospMetaDataJSON ) var ( - ospNetworkDataFile = ospMetaDataDir + "/network_data.json" - ospMetaDataFile = ospMetaDataDir + "/meta_data.json" + ospNetworkDataFile = ospMetaDataDir + "/" + ospNetworkDataJSON + ospMetaDataFile = ospMetaDataDir + "/" + ospMetaDataJSON ) +//go:generate ../../../bin/mockgen -destination mock/mock_openstack.go -source openstack.go +type OpenstackInterface interface { + CreateOpenstackDevicesInfo() error + CreateOpenstackDevicesInfoFromNodeStatus(*sriovnetworkv1.SriovNetworkNodeState) + DiscoverSriovDevicesVirtual() ([]sriovnetworkv1.InterfaceExt, error) +} + +type openstackContext struct { + hostManager host.HostManagerInterface + openStackDevicesInfo OSPDevicesInfo +} + // OSPMetaDataDevice -- Device structure within meta_data.json type OSPMetaDataDevice struct { Vlan int `json:"vlan,omitempty"` @@ -117,8 +102,12 @@ type OSPDeviceInfo struct { NetworkID string } +func New() OpenstackInterface { + return &openstackContext{} +} + // GetOpenstackData gets the metadata and network_data -func GetOpenstackData(useHostPath bool) (metaData *OSPMetaData, networkData *OSPNetworkData, err error) { +func getOpenstackData(useHostPath bool) (metaData *OSPMetaData, networkData *OSPNetworkData, err error) { metaData, networkData, err = getOpenstackDataFromConfigDrive(useHostPath) if err != nil { metaData, networkData, err = getOpenstackDataFromMetadataService() @@ -267,11 +256,19 @@ func getPCIAddressFromMACAddress(macAddress string, nics []*net.NIC) (string, er } // CreateOpenstackDevicesInfo create the openstack device info map -func CreateOpenstackDevicesInfo(metaData *OSPMetaData, networkData *OSPNetworkData) (OSPDevicesInfo, error) { +func (o *openstackContext) CreateOpenstackDevicesInfo() error { log.Log.Info("CreateOpenstackDevicesInfo()") devicesInfo := make(OSPDevicesInfo) + + metaData, networkData, err := getOpenstackData(true) + if err != nil { + log.Log.Error(err, "failed to read OpenStack data") + return err + } + if metaData == nil || networkData == nil { - return nil, nil + o.openStackDevicesInfo = make(OSPDevicesInfo) + return nil } // use this for hw pass throw interfaces @@ -291,12 +288,12 @@ func CreateOpenstackDevicesInfo(metaData *OSPMetaData, networkData *OSPNetworkDa // for vhostuser interface type we check the interfaces on the node pci, err := ghw.PCI() if err != nil { - return nil, fmt.Errorf("CreateOpenstackDevicesInfo(): error getting PCI info: %v", err) + return fmt.Errorf("CreateOpenstackDevicesInfo(): error getting PCI info: %v", err) } devices := pci.ListDevices() if len(devices) == 0 { - return nil, fmt.Errorf("CreateOpenstackDevicesInfo(): could not retrieve PCI devices") + return fmt.Errorf("CreateOpenstackDevicesInfo(): could not retrieve PCI devices") } for _, device := range devices { @@ -311,14 +308,14 @@ func CreateOpenstackDevicesInfo(metaData *OSPMetaData, networkData *OSPNetworkDa "device", device) continue } - if devClass != netClass { + if devClass != consts.NetClass { // Not network device continue } macAddress := "" - if name := tryToGetVirtualInterfaceName(device.Address); name != "" { - if mac := getNetDevMac(name); mac != "" { + if name := o.hostManager.TryToGetVirtualInterfaceName(device.Address); name != "" { + if mac := o.hostManager.GetNetDevMac(name); mac != "" { macAddress = mac } } @@ -339,11 +336,12 @@ func CreateOpenstackDevicesInfo(metaData *OSPMetaData, networkData *OSPNetworkDa } } - return devicesInfo, err + o.openStackDevicesInfo = devicesInfo + return nil } // DiscoverSriovDevicesVirtual discovers VFs on a virtual platform -func DiscoverSriovDevicesVirtual(devicesInfo OSPDevicesInfo) ([]sriovnetworkv1.InterfaceExt, error) { +func (o *openstackContext) DiscoverSriovDevicesVirtual() ([]sriovnetworkv1.InterfaceExt, error) { log.Log.V(2).Info("DiscoverSriovDevicesVirtual()") pfList := []sriovnetworkv1.InterfaceExt{} @@ -364,12 +362,12 @@ func DiscoverSriovDevicesVirtual(devicesInfo OSPDevicesInfo) ([]sriovnetworkv1.I "device", device) continue } - if devClass != netClass { + if devClass != consts.NetClass { // Not network device continue } - deviceInfo, exist := devicesInfo[device.Address] + deviceInfo, exist := o.openStackDevicesInfo[device.Address] if !exist { log.Log.Error(nil, "DiscoverSriovDevicesVirtual(): unable to find device in devicesInfo list, skipping", "device", device.Address) @@ -391,17 +389,17 @@ func DiscoverSriovDevicesVirtual(devicesInfo OSPDevicesInfo) ([]sriovnetworkv1.I DeviceID: device.Product.ID, NetFilter: netFilter, } - if mtu := getNetdevMTU(device.Address); mtu > 0 { + if mtu := o.hostManager.GetNetdevMTU(device.Address); mtu > 0 { iface.Mtu = mtu } - if name := tryToGetVirtualInterfaceName(device.Address); name != "" { + if name := o.hostManager.TryToGetVirtualInterfaceName(device.Address); name != "" { iface.Name = name - if iface.Mac = getNetDevMac(name); iface.Mac == "" { + if iface.Mac = o.hostManager.GetNetDevMac(name); iface.Mac == "" { iface.Mac = metaMac } - iface.LinkSpeed = getNetDevLinkSpeed(name) + iface.LinkSpeed = o.hostManager.GetNetDevLinkSpeed(name) } - iface.LinkType = getLinkType(iface) + iface.LinkType = o.hostManager.GetLinkType(iface) iface.TotalVfs = 1 iface.NumVfs = 1 @@ -422,147 +420,11 @@ func DiscoverSriovDevicesVirtual(devicesInfo OSPDevicesInfo) ([]sriovnetworkv1.I return pfList, nil } -func CreateOpenstackDevicesInfoFromNodeStatus(networkState *sriovnetworkv1.SriovNetworkNodeState) OSPDevicesInfo { +func (o *openstackContext) CreateOpenstackDevicesInfoFromNodeStatus(networkState *sriovnetworkv1.SriovNetworkNodeState) { devicesInfo := make(OSPDevicesInfo) for _, iface := range networkState.Status.Interfaces { devicesInfo[iface.PciAddress] = &OSPDeviceInfo{MacAddress: iface.Mac, NetworkID: iface.NetFilter} } - return devicesInfo -} - -// tryToGetVirtualInterfaceName get the interface name of a virtio interface -func tryToGetVirtualInterfaceName(pciAddr string) string { - log.Log.Info("tryToGetVirtualInterfaceName() get interface name for device", "device", pciAddr) - - // To support different driver that is not virtio-pci like mlx - name := tryGetInterfaceName(pciAddr) - if name != "" { - return name - } - - netDir, err := filepath.Glob(filepath.Join(sysBusPciDevices, pciAddr, "virtio*", "net")) - if err != nil || len(netDir) < 1 { - return "" - } - - fInfos, err := os.ReadDir(netDir[0]) - if err != nil { - log.Log.Error(err, "tryToGetVirtualInterfaceName(): failed to read net directory", "dir", netDir[0]) - return "" - } - - names := make([]string, 0) - for _, f := range fInfos { - names = append(names, f.Name()) - } - - if len(names) < 1 { - return "" - } - - return names[0] -} - -// SyncNodeStateVirtual attempt to update the node state to match the desired state -// -// in virtual platforms -func SyncNodeStateVirtual(newState *sriovnetworkv1.SriovNetworkNodeState) error { - var err error - for _, ifaceStatus := range newState.Status.Interfaces { - for _, iface := range newState.Spec.Interfaces { - if iface.PciAddress == ifaceStatus.PciAddress { - if !needUpdateVirtual(&iface, &ifaceStatus) { - log.Log.V(2).Info("SyncNodeStateVirtual(): no need update interface", "address", iface.PciAddress) - break - } - if err = configSriovDeviceVirtual(&iface, &ifaceStatus); err != nil { - log.Log.Error(err, "SyncNodeStateVirtual(): fail to config sriov interface", "address", iface.PciAddress) - return err - } - break - } - } - } - return nil -} - -func needUpdateVirtual(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) bool { - // The device MTU is set by the platorm - // The NumVfs is always 1 - if iface.NumVfs > 0 { - for _, vf := range ifaceStatus.VFs { - ingroup := false - for _, group := range iface.VfGroups { - if sriovnetworkv1.IndexInRange(vf.VfID, group.VfRange) { - ingroup = true - if group.DeviceType != constants.DeviceTypeNetDevice { - if group.DeviceType != vf.Driver { - log.Log.V(2).Info("needUpdateVirtual(): Driver needs update", - "desired", group.DeviceType, "current", vf.Driver) - return true - } - } else { - if sriovnetworkv1.StringInArray(vf.Driver, DpdkDrivers) { - log.Log.V(2).Info("needUpdateVirtual(): Driver needs update", - "desired", group.DeviceType, "current", vf.Driver) - return true - } - } - break - } - } - if !ingroup && sriovnetworkv1.StringInArray(vf.Driver, DpdkDrivers) { - // VF which has DPDK driver loaded but not in any group, needs to be reset to default driver. - return true - } - } - } - return false -} - -func configSriovDeviceVirtual(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) error { - log.Log.V(2).Info("configSriovDeviceVirtual(): config interface", "address", iface.PciAddress, "config", iface) - // Config VFs - if iface.NumVfs > 0 { - if iface.NumVfs > 1 { - log.Log.Error(nil, "configSriovDeviceVirtual(): in a virtual environment, only one VF per interface", - "numVfs", iface.NumVfs) - return errors.New("NumVfs > 1") - } - if len(iface.VfGroups) != 1 { - log.Log.Error(nil, "configSriovDeviceVirtual(): missing VFGroup") - return errors.New("NumVfs != 1") - } - addr := iface.PciAddress - log.Log.V(2).Info("configSriovDeviceVirtual()", "address", addr) - driver := "" - vfID := 0 - for _, group := range iface.VfGroups { - log.Log.V(2).Info("configSriovDeviceVirtual()", "group", group) - if sriovnetworkv1.IndexInRange(vfID, group.VfRange) { - log.Log.V(2).Info("configSriovDeviceVirtual()", "indexInRange", vfID) - if sriovnetworkv1.StringInArray(group.DeviceType, DpdkDrivers) { - log.Log.V(2).Info("configSriovDeviceVirtual()", "driver", group.DeviceType) - driver = group.DeviceType - } - break - } - } - if driver == "" { - log.Log.V(2).Info("configSriovDeviceVirtual(): bind default") - if err := BindDefaultDriver(addr); err != nil { - log.Log.Error(err, "configSriovDeviceVirtual(): fail to bind default driver", "device", addr) - return err - } - } else { - log.Log.V(2).Info("configSriovDeviceVirtual(): bind driver", "driver", driver) - if err := BindDpdkDriver(addr, driver); err != nil { - log.Log.Error(err, "configSriovDeviceVirtual(): fail to bind driver for device", - "driver", driver, "device", addr) - return err - } - } - } - return nil + o.openStackDevicesInfo = devicesInfo } diff --git a/pkg/utils/utils_virtual_test.go b/pkg/platforms/openstack/openstack_test.go similarity index 95% rename from pkg/utils/utils_virtual_test.go rename to pkg/platforms/openstack/openstack_test.go index 5c55d4b54..ca18bce6e 100644 --- a/pkg/utils/utils_virtual_test.go +++ b/pkg/platforms/openstack/openstack_test.go @@ -1,4 +1,4 @@ -package utils +package openstack import ( "testing" @@ -6,10 +6,11 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "k8s.io/utils/pointer" + "github.com/jaypipes/ghw" "github.com/jaypipes/ghw/pkg/net" "github.com/jaypipes/ghw/pkg/option" - "k8s.io/utils/pointer" ) func TestUtilsVirtual(t *testing.T) { @@ -44,7 +45,7 @@ var _ = Describe("Virtual", func() { ghw.Network = net.New }) - metaData, _, err := GetOpenstackData(false) + metaData, _, err := getOpenstackData(false) Expect(err).ToNot(HaveOccurred()) Expect(metaData.Devices).To(HaveLen(2)) diff --git a/pkg/utils/testdata/meta_data.json b/pkg/platforms/openstack/testdata/meta_data.json similarity index 100% rename from pkg/utils/testdata/meta_data.json rename to pkg/platforms/openstack/testdata/meta_data.json diff --git a/pkg/utils/testdata/network_data.json b/pkg/platforms/openstack/testdata/network_data.json similarity index 100% rename from pkg/utils/testdata/network_data.json rename to pkg/platforms/openstack/testdata/network_data.json diff --git a/pkg/platforms/platforms.go b/pkg/platforms/platforms.go new file mode 100644 index 000000000..529f51821 --- /dev/null +++ b/pkg/platforms/platforms.go @@ -0,0 +1,31 @@ +package platforms + +import ( + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openstack" +) + +//go:generate ../../bin/mockgen -destination mock/mock_platforms.go -source platforms.go +type Interface interface { + openshift.OpenshiftContextInterface + openstack.OpenstackInterface +} + +type platformHelper struct { + openshift.OpenshiftContextInterface + openstack.OpenstackInterface +} + +func NewDefaultPlatformHelper() (Interface, error) { + openshiftContext, err := openshift.New() + if err != nil { + return nil, err + } + + openstackContext := openstack.New() + + return &platformHelper{ + openshiftContext, + openstackContext, + }, nil +} diff --git a/pkg/plugins/generic/generic_plugin.go b/pkg/plugins/generic/generic_plugin.go index 44069407e..88aa38549 100644 --- a/pkg/plugins/generic/generic_plugin.go +++ b/pkg/plugins/generic/generic_plugin.go @@ -3,6 +3,7 @@ package generic import ( "bytes" "errors" + "fmt" "os/exec" "reflect" "strconv" @@ -12,10 +13,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + mlx "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" ) var PluginName = "generic_plugin" @@ -54,34 +57,33 @@ type GenericPlugin struct { LastState *sriovnetworkv1.SriovNetworkNodeState DriverStateMap DriverStateMapType DesiredKernelArgs map[string]bool - RunningOnHost bool - HostManager host.HostManagerInterface - StoreManager utils.StoreManagerInterface + pfsToSkip map[string]bool + helpers helper.HostHelpersInterface } const scriptsPath = "bindata/scripts/enable-kargs.sh" // Initialize our plugin and set up initial values -func NewGenericPlugin(runningOnHost bool, hostManager host.HostManagerInterface, storeManager utils.StoreManagerInterface) (plugin.VendorPlugin, error) { +func NewGenericPlugin(helpers helper.HostHelpersInterface) (plugin.VendorPlugin, error) { driverStateMap := make(map[uint]*DriverState) driverStateMap[Vfio] = &DriverState{ DriverName: vfioPciDriver, - DeviceType: constants.DeviceTypeVfioPci, + DeviceType: consts.DeviceTypeVfioPci, VdpaType: "", NeedDriverFunc: needDriverCheckDeviceType, DriverLoaded: false, } driverStateMap[VirtioVdpa] = &DriverState{ DriverName: virtioVdpaDriver, - DeviceType: constants.DeviceTypeNetDevice, - VdpaType: constants.VdpaTypeVirtio, + DeviceType: consts.DeviceTypeNetDevice, + VdpaType: consts.VdpaTypeVirtio, NeedDriverFunc: needDriverCheckVdpaType, DriverLoaded: false, } driverStateMap[VhostVdpa] = &DriverState{ DriverName: vhostVdpaDriver, - DeviceType: constants.DeviceTypeNetDevice, - VdpaType: constants.VdpaTypeVhost, + DeviceType: consts.DeviceTypeNetDevice, + VdpaType: consts.VdpaTypeVhost, NeedDriverFunc: needDriverCheckVdpaType, DriverLoaded: false, } @@ -90,9 +92,8 @@ func NewGenericPlugin(runningOnHost bool, hostManager host.HostManagerInterface, SpecVersion: "1.0", DriverStateMap: driverStateMap, DesiredKernelArgs: make(map[string]bool), - RunningOnHost: runningOnHost, - HostManager: hostManager, - StoreManager: storeManager, + pfsToSkip: make(map[string]bool), + helpers: helpers, }, nil } @@ -109,9 +110,6 @@ func (p *GenericPlugin) Spec() string { // OnNodeStateChange Invoked when SriovNetworkNodeState CR is created or updated, return if need drain and/or reboot node func (p *GenericPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeState) (needDrain bool, needReboot bool, err error) { log.Log.Info("generic-plugin OnNodeStateChange()") - needDrain = false - needReboot = false - err = nil p.DesireState = new needDrain = p.needDrainNode(new.Spec.Interfaces, new.Status.Interfaces) @@ -130,7 +128,7 @@ func (p *GenericPlugin) syncDriverState() error { for _, driverState := range p.DriverStateMap { if !driverState.DriverLoaded && driverState.NeedDriverFunc(p.DesireState, driverState) { log.Log.V(2).Info("loading driver", "name", driverState.DriverName) - if err := p.HostManager.LoadKernelModule(driverState.DriverName); err != nil { + if err := p.helpers.LoadKernelModule(driverState.DriverName); err != nil { log.Log.Error(err, "generic-plugin syncDriverState(): fail to load kmod", "name", driverState.DriverName) return err } @@ -156,27 +154,19 @@ func (p *GenericPlugin) Apply() error { return err } - // Create a map with all the PFs we will need to configure - // we need to create it here before we access the host file system using the chroot function - // because the skipConfigVf needs the mstconfig package that exist only inside the sriov-config-daemon file system - pfsToSkip, err := utils.GetPfsToSkip(p.DesireState) - if err != nil { - return err - } - // When calling from systemd do not try to chroot - if !p.RunningOnHost { - exit, err := utils.Chroot("/host") + if !vars.UsingSystemdMode { + exit, err := p.helpers.Chroot(consts.Host) if err != nil { return err } defer exit() } - if err := utils.SyncNodeState(p.DesireState, pfsToSkip); err != nil { + if err := p.helpers.ConfigSriovInterfaces(p.helpers, p.DesireState.Spec.Interfaces, p.DesireState.Status.Interfaces, p.pfsToSkip); err != nil { // Catch the "cannot allocate memory" error and try to use PCI realloc if errors.Is(err, syscall.ENOMEM) { - p.addToDesiredKernelArgs(utils.KernelArgPciRealloc) + p.addToDesiredKernelArgs(consts.KernelArgPciRealloc) } return err } @@ -217,7 +207,7 @@ func setKernelArg(karg string) (bool, error) { if err := cmd.Run(); err != nil { // if grubby is not there log and assume kernel args are set correctly. - if isCommandNotFound(err) { + if utils.IsCommandNotFound(err) { log.Log.Error(err, "generic-plugin setKernelArg(): grubby or ostree command not found. Please ensure that kernel arg are set", "kargs", karg) return false, nil @@ -236,15 +226,6 @@ func setKernelArg(karg string) (bool, error) { return false, err } -func isCommandNotFound(err error) bool { - if exitErr, ok := err.(*exec.ExitError); ok { - if status, ok := exitErr.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 127 { - return true - } - } - return false -} - // addToDesiredKernelArgs Should be called to queue a kernel arg to be added to the node. func (p *GenericPlugin) addToDesiredKernelArgs(karg string) { if _, ok := p.DesiredKernelArgs[karg]; !ok { @@ -259,12 +240,12 @@ func (p *GenericPlugin) syncDesiredKernelArgs() (bool, error) { if len(p.DesiredKernelArgs) == 0 { return false, nil } - kargs, err := utils.GetCurrentKernelArgs(false) + kargs, err := p.helpers.GetCurrentKernelArgs() if err != nil { return false, err } for desiredKarg, attempted := range p.DesiredKernelArgs { - set := utils.IsKernelArgsSet(kargs, desiredKarg) + set := p.helpers.IsKernelArgsSet(kargs, desiredKarg) if !set { if attempted { log.Log.V(2).Info("generic-plugin syncDesiredKernelArgs(): previously attempted to set kernel arg", @@ -302,7 +283,7 @@ func (p *GenericPlugin) needDrainNode(desired sriovnetworkv1.Interfaces, current "address", iface.PciAddress) break } - if utils.NeedUpdate(&iface, &ifaceStatus) { + if sriovnetworkv1.NeedToUpdateSriov(&iface, &ifaceStatus) { log.Log.V(2).Info("generic-plugin needDrainNode(): need drain, for PCI address request update", "address", iface.PciAddress) needDrain = true @@ -314,7 +295,7 @@ func (p *GenericPlugin) needDrainNode(desired sriovnetworkv1.Interfaces, current } if !configured && ifaceStatus.NumVfs > 0 { // load the PF info - pfStatus, exist, err := p.StoreManager.LoadPfsStatus(ifaceStatus.PciAddress) + pfStatus, exist, err := p.helpers.LoadPfsStatus(ifaceStatus.PciAddress) if err != nil { log.Log.Error(err, "generic-plugin needDrainNode(): failed to load info about PF status for pci device", "address", ifaceStatus.PciAddress) @@ -347,8 +328,8 @@ func (p *GenericPlugin) needDrainNode(desired sriovnetworkv1.Interfaces, current func (p *GenericPlugin) addVfioDesiredKernelArg(state *sriovnetworkv1.SriovNetworkNodeState) { driverState := p.DriverStateMap[Vfio] if !driverState.DriverLoaded && driverState.NeedDriverFunc(state, driverState) { - p.addToDesiredKernelArgs(utils.KernelArgIntelIommu) - p.addToDesiredKernelArgs(utils.KernelArgIommuPt) + p.addToDesiredKernelArgs(consts.KernelArgIntelIommu) + p.addToDesiredKernelArgs(consts.KernelArgIommuPt) } } @@ -366,7 +347,16 @@ func (p *GenericPlugin) needRebootNode(state *sriovnetworkv1.SriovNetworkNodeSta needReboot = true } - updateNode, err = utils.WriteSwitchdevConfFile(state) + // Create a map with all the PFs we will need to configure + // we need to create it here before we access the host file system using the chroot function + // because the skipConfigVf needs the mstconfig package that exist only inside the sriov-config-daemon file system + pfsToSkip, err := getPfsToSkip(p.DesireState, p.helpers) + if err != nil { + return false, err + } + p.pfsToSkip = pfsToSkip + + updateNode, err = p.helpers.WriteSwitchdevConfFile(state, p.pfsToSkip) if err != nil { log.Log.Error(err, "generic-plugin needRebootNode(): fail to write switchdev device config file") return false, err @@ -379,6 +369,53 @@ func (p *GenericPlugin) needRebootNode(state *sriovnetworkv1.SriovNetworkNodeSta return needReboot, nil } +// getPfsToSkip return a map of devices pci addresses to should be configured via systemd instead if the legacy mode +// we skip devices in switchdev mode and Bluefield card in ConnectX mode +func getPfsToSkip(ns *sriovnetworkv1.SriovNetworkNodeState, mlxHelper mlx.MellanoxInterface) (map[string]bool, error) { + pfsToSkip := map[string]bool{} + for _, ifaceStatus := range ns.Status.Interfaces { + for _, iface := range ns.Spec.Interfaces { + if iface.PciAddress == ifaceStatus.PciAddress { + skip, err := skipConfigVf(iface, ifaceStatus, mlxHelper) + if err != nil { + log.Log.Error(err, "GetPfsToSkip(): fail to check for skip VFs", "device", iface.PciAddress) + return pfsToSkip, err + } + pfsToSkip[iface.PciAddress] = skip + break + } + } + } + + return pfsToSkip, nil +} + +// skipConfigVf Use systemd service to configure switchdev mode or BF-2 NICs in OpenShift +func skipConfigVf(ifSpec sriovnetworkv1.Interface, ifStatus sriovnetworkv1.InterfaceExt, mlxHelper mlx.MellanoxInterface) (bool, error) { + if ifSpec.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { + log.Log.V(2).Info("skipConfigVf(): skip config VF for switchdev device") + return true, nil + } + + // NVIDIA BlueField 2 and BlueField3 in OpenShift + if vars.ClusterType == consts.ClusterTypeOpenshift && ifStatus.Vendor == mlx.VendorMellanox && (ifStatus.DeviceID == mlx.DeviceBF2 || ifStatus.DeviceID == mlx.DeviceBF3) { + // TODO: remove this when switch to the systemd configuration support. + mode, err := mlxHelper.GetMellanoxBlueFieldMode(ifStatus.PciAddress) + if err != nil { + return false, fmt.Errorf("failed to read Mellanox Bluefield card mode for %s,%v", ifStatus.PciAddress, err) + } + + if mode == mlx.BluefieldConnectXMode { + return false, nil + } + + log.Log.V(2).Info("skipConfigVf(): skip config VF for Bluefiled card on DPU mode") + return true, nil + } + + return false, nil +} + // ////////////// for testing purposes only /////////////////////// func (p *GenericPlugin) getDriverStateMap() DriverStateMapType { return p.DriverStateMap diff --git a/pkg/plugins/generic/generic_plugin_test.go b/pkg/plugins/generic/generic_plugin_test.go index e1211b392..881fc87d4 100644 --- a/pkg/plugins/generic/generic_plugin_test.go +++ b/pkg/plugins/generic/generic_plugin_test.go @@ -8,9 +8,8 @@ import ( . "github.com/onsi/gomega" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - mock_host "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/mock" + mock_helper "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper/mock" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" - mock_utils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils/mock" ) func TestGenericPlugin(t *testing.T) { @@ -24,16 +23,16 @@ var _ = Describe("Generic plugin", func() { genericPlugin plugin.VendorPlugin err error ctrl *gomock.Controller - mockHost *mock_host.MockHostManagerInterface - mockStore *mock_utils.MockStoreManagerInterface + hostHelper *mock_helper.MockHostHelpersInterface ) BeforeEach(func() { t = GinkgoT() ctrl = gomock.NewController(t) - mockHost = mock_host.NewMockHostManagerInterface(ctrl) - mockStore = mock_utils.NewMockStoreManagerInterface(ctrl) - genericPlugin, err = NewGenericPlugin(false, mockHost, mockStore) + + hostHelper = mock_helper.NewMockHostHelpersInterface(ctrl) + + genericPlugin, err = NewGenericPlugin(hostHelper) Expect(err).ToNot(HaveOccurred()) }) @@ -79,6 +78,7 @@ var _ = Describe("Generic plugin", func() { }, } + hostHelper.EXPECT().WriteSwitchdevConfFile(networkNodeState, map[string]bool{"0000:00:00.0": false}).Return(false, nil) needDrain, needReboot, err := genericPlugin.OnNodeStateChange(networkNodeState) Expect(err).ToNot(HaveOccurred()) Expect(needReboot).To(BeFalse()) @@ -134,6 +134,7 @@ var _ = Describe("Generic plugin", func() { }, } + hostHelper.EXPECT().WriteSwitchdevConfFile(networkNodeState, map[string]bool{"0000:00:00.0": false}).Return(false, nil) needDrain, needReboot, err := genericPlugin.OnNodeStateChange(networkNodeState) Expect(err).ToNot(HaveOccurred()) Expect(needReboot).To(BeFalse()) diff --git a/pkg/plugins/intel/intel_plugin.go b/pkg/plugins/intel/intel_plugin.go index bf032652e..1c64a47fb 100644 --- a/pkg/plugins/intel/intel_plugin.go +++ b/pkg/plugins/intel/intel_plugin.go @@ -4,6 +4,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" ) @@ -16,7 +17,7 @@ type IntelPlugin struct { LastState *sriovnetworkv1.SriovNetworkNodeState } -func NewIntelPlugin() (plugin.VendorPlugin, error) { +func NewIntelPlugin(helpers helper.HostHelpersInterface) (plugin.VendorPlugin, error) { return &IntelPlugin{ PluginName: PluginName, SpecVersion: "1.0", diff --git a/pkg/plugins/k8s/k8s_plugin.go b/pkg/plugins/k8s/k8s_plugin.go index 4cbcc818e..02b4e6336 100644 --- a/pkg/plugins/k8s/k8s_plugin.go +++ b/pkg/plugins/k8s/k8s_plugin.go @@ -6,13 +6,14 @@ import ( "path" "strings" - "github.com/coreos/go-systemd/v22/unit" "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" + hostTypes "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" plugins "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/service" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) var PluginName = "k8s_plugin" @@ -20,17 +21,16 @@ var PluginName = "k8s_plugin" type K8sPlugin struct { PluginName string SpecVersion string - serviceManager service.ServiceManager - switchdevBeforeNMRunScript *service.ScriptManifestFile - switchdevAfterNMRunScript *service.ScriptManifestFile - switchdevUdevScript *service.ScriptManifestFile - switchdevBeforeNMService *service.Service - switchdevAfterNMService *service.Service - openVSwitchService *service.Service - networkManagerService *service.Service - sriovService *service.Service + switchdevBeforeNMRunScript *hostTypes.ScriptManifestFile + switchdevAfterNMRunScript *hostTypes.ScriptManifestFile + switchdevUdevScript *hostTypes.ScriptManifestFile + switchdevBeforeNMService *hostTypes.Service + switchdevAfterNMService *hostTypes.Service + openVSwitchService *hostTypes.Service + networkManagerService *hostTypes.Service + sriovService *hostTypes.Service updateTarget *k8sUpdateTarget - useSystemdService bool + hostHelper helper.HostHelpersInterface } type k8sUpdateTarget struct { @@ -40,7 +40,7 @@ type k8sUpdateTarget struct { switchdevAfterNMRunScript bool switchdevUdevScript bool sriovScript bool - systemServices []*service.Service + systemServices []*hostTypes.Service } func (u *k8sUpdateTarget) needUpdate() bool { @@ -58,7 +58,7 @@ func (u *k8sUpdateTarget) reset() { u.switchdevAfterNMRunScript = false u.switchdevUdevScript = false u.sriovScript = false - u.systemServices = []*service.Service{} + u.systemServices = []*hostTypes.Service{} } func (u *k8sUpdateTarget) String() string { @@ -92,18 +92,15 @@ const ( configuresSwitchdevBeforeNMScript = switchdevManifestPath + "files/switchdev-configuration-before-nm.sh.yaml" configuresSwitchdevAfterNMScript = switchdevManifestPath + "files/switchdev-configuration-after-nm.sh.yaml" switchdevRenamingUdevScript = switchdevManifestPath + "files/switchdev-vf-link-name.sh.yaml" - - chroot = "/host" ) // Initialize our plugin and set up initial values -func NewK8sPlugin(useSystemdService bool) (plugins.VendorPlugin, error) { +func NewK8sPlugin(helper helper.HostHelpersInterface) (plugins.VendorPlugin, error) { k8sPluging := &K8sPlugin{ - PluginName: PluginName, - SpecVersion: "1.0", - serviceManager: service.NewServiceManager(chroot), - updateTarget: &k8sUpdateTarget{}, - useSystemdService: useSystemdService, + PluginName: PluginName, + SpecVersion: "1.0", + hostHelper: helper, + updateTarget: &k8sUpdateTarget{}, } return k8sPluging, k8sPluging.readManifestFiles() @@ -128,11 +125,11 @@ func (p *K8sPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeState) p.updateTarget.reset() // TODO add check for enableOvsOffload in OperatorConfig later // Update services if switchdev required - if !p.useSystemdService && !utils.IsSwitchdevModeSpec(new.Spec) { + if !vars.UsingSystemdMode && !sriovnetworkv1.IsSwitchdevModeSpec(new.Spec) { return } - if utils.IsSwitchdevModeSpec(new.Spec) { + if sriovnetworkv1.IsSwitchdevModeSpec(new.Spec) { // Check services err = p.switchDevServicesStateUpdate() if err != nil { @@ -141,7 +138,7 @@ func (p *K8sPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeState) } } - if p.useSystemdService { + if vars.UsingSystemdMode { // Check sriov service err = p.sriovServiceStateUpdate() if err != nil { @@ -170,14 +167,14 @@ func (p *K8sPlugin) Apply() error { return err } - if p.useSystemdService { + if vars.UsingSystemdMode { if err := p.updateSriovService(); err != nil { return err } } for _, systemService := range p.updateTarget.systemServices { - if err := p.updateSystemService(systemService); err != nil { + if err := p.hostHelper.UpdateSystemService(systemService); err != nil { return err } } @@ -187,26 +184,20 @@ func (p *K8sPlugin) Apply() error { func (p *K8sPlugin) readSwitchdevManifest() error { // Read switchdev service - switchdevBeforeNMService, err := service.ReadServiceManifestFile(switchdevBeforeNMUnitFile) + switchdevBeforeNMService, err := p.hostHelper.ReadServiceManifestFile(switchdevBeforeNMUnitFile) if err != nil { return err } - switchdevAfterNMService, err := service.ReadServiceManifestFile(switchdevAfterNMUnitFile) + switchdevAfterNMService, err := p.hostHelper.ReadServiceManifestFile(switchdevAfterNMUnitFile) if err != nil { return err } - // Remove run condition form the service - conditionOpt := &unit.UnitOption{ - Section: "Unit", - Name: "ConditionPathExists", - Value: "!/etc/ignition-machine-config-encapsulated.json", - } - switchdevBeforeNMService, err = service.RemoveFromService(switchdevBeforeNMService, conditionOpt) + switchdevBeforeNMService, err = p.hostHelper.RemoveFromService(switchdevBeforeNMService, hostTypes.ConditionOpt) if err != nil { return err } - switchdevAfterNMService, err = service.RemoveFromService(switchdevAfterNMService, conditionOpt) + switchdevAfterNMService, err = p.hostHelper.RemoveFromService(switchdevAfterNMService, hostTypes.ConditionOpt) if err != nil { return err } @@ -214,11 +205,11 @@ func (p *K8sPlugin) readSwitchdevManifest() error { p.switchdevAfterNMService = switchdevAfterNMService // Read switchdev run script - switchdevBeforeNMRunScript, err := service.ReadScriptManifestFile(configuresSwitchdevBeforeNMScript) + switchdevBeforeNMRunScript, err := p.hostHelper.ReadScriptManifestFile(configuresSwitchdevBeforeNMScript) if err != nil { return err } - switchdevAfterNMRunScript, err := service.ReadScriptManifestFile(configuresSwitchdevAfterNMScript) + switchdevAfterNMRunScript, err := p.hostHelper.ReadScriptManifestFile(configuresSwitchdevAfterNMScript) if err != nil { return err } @@ -226,7 +217,7 @@ func (p *K8sPlugin) readSwitchdevManifest() error { p.switchdevAfterNMRunScript = switchdevAfterNMRunScript // Read switchdev udev script - switchdevUdevScript, err := service.ReadScriptManifestFile(switchdevRenamingUdevScript) + switchdevUdevScript, err := p.hostHelper.ReadScriptManifestFile(switchdevRenamingUdevScript) if err != nil { return err } @@ -236,7 +227,7 @@ func (p *K8sPlugin) readSwitchdevManifest() error { } func (p *K8sPlugin) readNetworkManagerManifest() error { - networkManagerService, err := service.ReadServiceInjectionManifestFile(networkManagerUnitFile) + networkManagerService, err := p.hostHelper.ReadServiceInjectionManifestFile(networkManagerUnitFile) if err != nil { return err } @@ -246,7 +237,7 @@ func (p *K8sPlugin) readNetworkManagerManifest() error { } func (p *K8sPlugin) readOpenVSwitchdManifest() error { - openVSwitchService, err := service.ReadServiceInjectionManifestFile(ovsUnitFile) + openVSwitchService, err := p.hostHelper.ReadServiceInjectionManifestFile(ovsUnitFile) if err != nil { return err } @@ -256,7 +247,7 @@ func (p *K8sPlugin) readOpenVSwitchdManifest() error { } func (p *K8sPlugin) readSriovServiceManifest() error { - sriovService, err := service.ReadServiceManifestFile(sriovUnitFile) + sriovService, err := p.hostHelper.ReadServiceManifestFile(sriovUnitFile) if err != nil { return err } @@ -322,7 +313,7 @@ func (p *K8sPlugin) switchdevServiceStateUpdate() error { func (p *K8sPlugin) sriovServiceStateUpdate() error { log.Log.Info("sriovServiceStateUpdate()") - isServiceEnabled, err := p.serviceManager.IsServiceEnabled(p.sriovService.Path) + isServiceEnabled, err := p.hostHelper.IsServiceEnabled(p.sriovService.Path) if err != nil { return err } @@ -340,12 +331,12 @@ func (p *K8sPlugin) sriovServiceStateUpdate() error { return nil } -func (p *K8sPlugin) getSwitchDevSystemServices() []*service.Service { - return []*service.Service{p.networkManagerService, p.openVSwitchService} +func (p *K8sPlugin) getSwitchDevSystemServices() []*hostTypes.Service { + return []*hostTypes.Service{p.networkManagerService, p.openVSwitchService} } -func (p *K8sPlugin) isSwitchdevScriptNeedUpdate(scriptObj *service.ScriptManifestFile) (needUpdate bool, err error) { - data, err := os.ReadFile(path.Join(chroot, scriptObj.Path)) +func (p *K8sPlugin) isSwitchdevScriptNeedUpdate(scriptObj *hostTypes.ScriptManifestFile) (needUpdate bool, err error) { + data, err := os.ReadFile(path.Join(consts.Host, scriptObj.Path)) if err != nil { if !os.IsNotExist(err) { return false, err @@ -357,8 +348,8 @@ func (p *K8sPlugin) isSwitchdevScriptNeedUpdate(scriptObj *service.ScriptManifes return false, nil } -func (p *K8sPlugin) isSwitchdevServiceNeedUpdate(serviceObj *service.Service) (needUpdate bool, err error) { - swdService, err := p.serviceManager.ReadService(serviceObj.Path) +func (p *K8sPlugin) isSwitchdevServiceNeedUpdate(serviceObj *hostTypes.Service) (needUpdate bool, err error) { + swdService, err := p.hostHelper.ReadService(serviceObj.Path) if err != nil { if !os.IsNotExist(err) { return false, err @@ -366,7 +357,7 @@ func (p *K8sPlugin) isSwitchdevServiceNeedUpdate(serviceObj *service.Service) (n // service not exists return true, nil } else { - needChange, err := service.CompareServices(swdService, serviceObj) + needChange, err := p.hostHelper.CompareServices(swdService, serviceObj) if err != nil { return false, err } @@ -374,16 +365,16 @@ func (p *K8sPlugin) isSwitchdevServiceNeedUpdate(serviceObj *service.Service) (n } } -func (p *K8sPlugin) isSystemServiceNeedUpdate(serviceObj *service.Service) bool { +func (p *K8sPlugin) isSystemServiceNeedUpdate(serviceObj *hostTypes.Service) bool { log.Log.Info("isSystemServiceNeedUpdate()") - systemService, err := p.serviceManager.ReadService(serviceObj.Path) + systemService, err := p.hostHelper.ReadService(serviceObj.Path) if err != nil { log.Log.Error(err, "k8s-plugin isSystemServiceNeedUpdate(): failed to read sriov-config service file, ignoring", "path", serviceObj.Path) return false } if systemService != nil { - needChange, err := service.CompareServices(systemService, serviceObj) + needChange, err := p.hostHelper.CompareServices(systemService, serviceObj) if err != nil { log.Log.Error(err, "k8s-plugin isSystemServiceNeedUpdate(): failed to compare sriov-config service, ignoring") return false @@ -395,9 +386,9 @@ func (p *K8sPlugin) isSystemServiceNeedUpdate(serviceObj *service.Service) bool } func (p *K8sPlugin) systemServicesStateUpdate() error { - var services []*service.Service + var services []*hostTypes.Service for _, systemService := range p.getSwitchDevSystemServices() { - exist, err := p.serviceManager.IsServiceExist(systemService.Path) + exist, err := p.hostHelper.IsServiceExist(systemService.Path) if err != nil { return err } @@ -431,7 +422,7 @@ func (p *K8sPlugin) switchDevServicesStateUpdate() error { func (p *K8sPlugin) updateSriovService() error { if p.updateTarget.sriovScript { - err := p.serviceManager.EnableService(p.sriovService) + err := p.hostHelper.EnableService(p.sriovService) if err != nil { return err } @@ -442,21 +433,21 @@ func (p *K8sPlugin) updateSriovService() error { func (p *K8sPlugin) updateSwitchdevService() error { if p.updateTarget.switchdevBeforeNMService { - err := p.serviceManager.EnableService(p.switchdevBeforeNMService) + err := p.hostHelper.EnableService(p.switchdevBeforeNMService) if err != nil { return err } } if p.updateTarget.switchdevAfterNMService { - err := p.serviceManager.EnableService(p.switchdevAfterNMService) + err := p.hostHelper.EnableService(p.switchdevAfterNMService) if err != nil { return err } } if p.updateTarget.switchdevBeforeNMRunScript { - err := os.WriteFile(path.Join(chroot, p.switchdevBeforeNMRunScript.Path), + err := os.WriteFile(path.Join(consts.Host, p.switchdevBeforeNMRunScript.Path), []byte(p.switchdevBeforeNMRunScript.Contents.Inline), 0755) if err != nil { return err @@ -464,7 +455,7 @@ func (p *K8sPlugin) updateSwitchdevService() error { } if p.updateTarget.switchdevAfterNMRunScript { - err := os.WriteFile(path.Join(chroot, p.switchdevAfterNMRunScript.Path), + err := os.WriteFile(path.Join(consts.Host, p.switchdevAfterNMRunScript.Path), []byte(p.switchdevAfterNMRunScript.Contents.Inline), 0755) if err != nil { return err @@ -472,7 +463,7 @@ func (p *K8sPlugin) updateSwitchdevService() error { } if p.updateTarget.switchdevUdevScript { - err := os.WriteFile(path.Join(chroot, p.switchdevUdevScript.Path), + err := os.WriteFile(path.Join(consts.Host, p.switchdevUdevScript.Path), []byte(p.switchdevUdevScript.Contents.Inline), 0755) if err != nil { return err @@ -481,32 +472,3 @@ func (p *K8sPlugin) updateSwitchdevService() error { return nil } - -func (p *K8sPlugin) updateSystemService(serviceObj *service.Service) error { - systemService, err := p.serviceManager.ReadService(serviceObj.Path) - if err != nil { - return err - } - if systemService == nil { - // Invalid case to reach here - return fmt.Errorf("k8s-plugin updateSystemService(): can't update non-existing service %q", serviceObj.Name) - } - serviceOptions, err := unit.Deserialize(strings.NewReader(serviceObj.Content)) - if err != nil { - return err - } - updatedService, err := service.AppendToService(systemService, serviceOptions...) - if err != nil { - return err - } - - return p.serviceManager.EnableService(updatedService) -} - -func (p *K8sPlugin) SetSystemdFlag() { - p.useSystemdService = true -} - -func (p *K8sPlugin) IsSystemService() bool { - return p.useSystemdService -} diff --git a/pkg/plugins/mellanox/mellanox_plugin.go b/pkg/plugins/mellanox/mellanox_plugin.go index 5f9f9f56a..a95d24b9d 100644 --- a/pkg/plugins/mellanox/mellanox_plugin.go +++ b/pkg/plugins/mellanox/mellanox_plugin.go @@ -2,15 +2,13 @@ package mellanox import ( "fmt" - "strconv" - "strings" "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + mlx "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" ) var PluginName = "mellanox_plugin" @@ -18,36 +16,21 @@ var PluginName = "mellanox_plugin" type MellanoxPlugin struct { PluginName string SpecVersion string + helpers helper.HostHelpersInterface } -type mlnxNic struct { - enableSriov bool - totalVfs int - linkTypeP1 string - linkTypeP2 string -} - -const ( - PreconfiguredLinkType = "Preconfigured" - UknownLinkType = "Uknown" - TotalVfs = "NUM_OF_VFS" - EnableSriov = "SRIOV_EN" - LinkTypeP1 = "LINK_TYPE_P1" - LinkTypeP2 = "LINK_TYPE_P2" - MellanoxVendorID = "15b3" -) - -var attributesToChange map[string]mlnxNic +var attributesToChange map[string]mlx.MlxNic var mellanoxNicsStatus map[string]map[string]sriovnetworkv1.InterfaceExt var mellanoxNicsSpec map[string]sriovnetworkv1.Interface // Initialize our plugin and set up initial values -func NewMellanoxPlugin() (plugin.VendorPlugin, error) { +func NewMellanoxPlugin(helpers helper.HostHelpersInterface) (plugin.VendorPlugin, error) { mellanoxNicsStatus = map[string]map[string]sriovnetworkv1.InterfaceExt{} return &MellanoxPlugin{ PluginName: PluginName, SpecVersion: "1.0", + helpers: helpers, }, nil } @@ -68,36 +51,35 @@ func (p *MellanoxPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeS needDrain = false needReboot = false err = nil - attributesToChange = map[string]mlnxNic{} + attributesToChange = map[string]mlx.MlxNic{} + mellanoxNicsStatus = map[string]map[string]sriovnetworkv1.InterfaceExt{} mellanoxNicsSpec = map[string]sriovnetworkv1.Interface{} processedNics := map[string]bool{} - // Read mellanox NIC status once - if len(mellanoxNicsStatus) == 0 { - for _, iface := range new.Status.Interfaces { - if iface.Vendor != MellanoxVendorID { - continue - } + // fill mellanoxNicsStatus + for _, iface := range new.Status.Interfaces { + if iface.Vendor != mlx.MellanoxVendorID { + continue + } - pciPrefix := getPciAddressPrefix(iface.PciAddress) - if ifaces, ok := mellanoxNicsStatus[pciPrefix]; ok { - ifaces[iface.PciAddress] = iface - } else { - mellanoxNicsStatus[pciPrefix] = map[string]sriovnetworkv1.InterfaceExt{iface.PciAddress: iface} - } + pciPrefix := mlx.GetPciAddressPrefix(iface.PciAddress) + if ifaces, ok := mellanoxNicsStatus[pciPrefix]; ok { + ifaces[iface.PciAddress] = iface + } else { + mellanoxNicsStatus[pciPrefix] = map[string]sriovnetworkv1.InterfaceExt{iface.PciAddress: iface} } } // Add only mellanox cards that required changes in the map, to help track dual port NICs for _, iface := range new.Spec.Interfaces { - pciPrefix := getPciAddressPrefix(iface.PciAddress) + pciPrefix := mlx.GetPciAddressPrefix(iface.PciAddress) if _, ok := mellanoxNicsStatus[pciPrefix]; !ok { continue } mellanoxNicsSpec[iface.PciAddress] = iface } - if utils.IsKernelLockdownMode(false) { + if p.helpers.IsKernelLockdownMode() { if len(mellanoxNicsSpec) > 0 { log.Log.Info("Lockdown mode detected, failing on interface update for mellanox devices") return false, false, fmt.Errorf("mellanox device detected when in lockdown mode") @@ -107,41 +89,45 @@ func (p *MellanoxPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeS } for _, ifaceSpec := range mellanoxNicsSpec { - pciPrefix := getPciAddressPrefix(ifaceSpec.PciAddress) + pciPrefix := mlx.GetPciAddressPrefix(ifaceSpec.PciAddress) // skip processed nics, help not running the same logic 2 times for dual port NICs if _, ok := processedNics[pciPrefix]; ok { continue } processedNics[pciPrefix] = true - fwCurrent, fwNext, err := getMlnxNicFwData(ifaceSpec.PciAddress) + fwCurrent, fwNext, err := p.helpers.GetMlxNicFwData(ifaceSpec.PciAddress) if err != nil { return false, false, err } - isDualPort := isDualPort(ifaceSpec.PciAddress) + isDualPort := mlx.IsDualPort(ifaceSpec.PciAddress, mellanoxNicsStatus) // Attributes to change - attrs := &mlnxNic{totalVfs: -1} + attrs := &mlx.MlxNic{TotalVfs: -1} var changeWithoutReboot bool - var totalVfs int - totalVfs, needReboot, changeWithoutReboot = handleTotalVfs(fwCurrent, fwNext, attrs, ifaceSpec, isDualPort) - sriovEnNeedReboot, sriovEnChangeWithoutReboot := handleEnableSriov(totalVfs, fwCurrent, fwNext, attrs) - needReboot = needReboot || sriovEnNeedReboot - changeWithoutReboot = changeWithoutReboot || sriovEnChangeWithoutReboot - - // failing as we can't the fwTotalVf is lower than the request one on a nic with externallyManage configured - if ifaceSpec.ExternallyManaged && needReboot { - return true, true, fmt.Errorf( - "interface %s required a change in the TotalVfs but the policy is externally managed failing: firmware TotalVf %d requested TotalVf %d", - ifaceSpec.PciAddress, fwCurrent.totalVfs, totalVfs) - } + totalVfs, totalVfsNeedReboot, totalVfsChangeWithoutReboot := mlx.HandleTotalVfs(fwCurrent, fwNext, attrs, ifaceSpec, isDualPort, mellanoxNicsSpec) + sriovEnNeedReboot, sriovEnChangeWithoutReboot := mlx.HandleEnableSriov(totalVfs, fwCurrent, fwNext, attrs) + needReboot = totalVfsNeedReboot || sriovEnNeedReboot + changeWithoutReboot = totalVfsChangeWithoutReboot || sriovEnChangeWithoutReboot - needLinkChange, err := handleLinkType(pciPrefix, fwCurrent, attrs) + needLinkChange, err := mlx.HandleLinkType(pciPrefix, fwCurrent, attrs, mellanoxNicsSpec, mellanoxNicsStatus) if err != nil { return false, false, err } - needReboot = needReboot || needLinkChange + + // no FW changes allowed when NIC is externally managed + if ifaceSpec.ExternallyManaged { + if totalVfsNeedReboot || totalVfsChangeWithoutReboot { + return false, false, fmt.Errorf( + "interface %s required a change in the TotalVfs but the policy is externally managed failing: firmware TotalVf %d requested TotalVf %d", + ifaceSpec.PciAddress, fwCurrent.TotalVfs, totalVfs) + } + if needLinkChange { + return false, false, fmt.Errorf("change required for link type but the policy is externally managed, failing") + } + } + if needReboot || changeWithoutReboot { attributesToChange[ifaceSpec.PciAddress] = *attrs } @@ -157,19 +143,24 @@ func (p *MellanoxPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeS processedNics[pciPrefix] = true pciAddress := pciPrefix + "0" + // Skip externally managed NICs + if p.nicHasExternallyManagedPFs(portsMap) { + continue + } + // Skip unsupported devices if id := sriovnetworkv1.GetVfDeviceID(portsMap[pciAddress].DeviceID); id == "" { continue } - _, fwNext, err := getMlnxNicFwData(pciAddress) + _, fwNext, err := p.helpers.GetMlxNicFwData(pciAddress) if err != nil { return false, false, err } - if fwNext.totalVfs > 0 || fwNext.enableSriov { - attributesToChange[pciAddress] = mlnxNic{totalVfs: 0} - log.Log.V(2).Info("Changing TotalVfs to 0, doesn't require rebooting", "fwNext.totalVfs", fwNext.totalVfs) + if fwNext.TotalVfs > 0 || fwNext.EnableSriov { + attributesToChange[pciAddress] = mlx.MlxNic{TotalVfs: 0} + log.Log.V(2).Info("Changing TotalVfs to 0, doesn't require rebooting", "fwNext.totalVfs", fwNext.TotalVfs) } } @@ -182,251 +173,30 @@ func (p *MellanoxPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeS // Apply config change func (p *MellanoxPlugin) Apply() error { - if utils.IsKernelLockdownMode(false) { + if p.helpers.IsKernelLockdownMode() { log.Log.Info("mellanox-plugin Apply() - skipping due to lockdown mode") return nil } log.Log.Info("mellanox-plugin Apply()") - return configFW() -} - -func configFW() error { - log.Log.Info("mellanox-plugin configFW()") - for pciAddr, fwArgs := range attributesToChange { - cmdArgs := []string{"-d", pciAddr, "-y", "set"} - if fwArgs.enableSriov { - cmdArgs = append(cmdArgs, fmt.Sprintf("%s=True", EnableSriov)) - } else if fwArgs.totalVfs == 0 { - cmdArgs = append(cmdArgs, fmt.Sprintf("%s=False", EnableSriov)) - } - if fwArgs.totalVfs > -1 { - cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%d", TotalVfs, fwArgs.totalVfs)) - } - if len(fwArgs.linkTypeP1) > 0 { - cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%s", LinkTypeP1, fwArgs.linkTypeP1)) - } - if len(fwArgs.linkTypeP2) > 0 { - cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%s", LinkTypeP2, fwArgs.linkTypeP2)) - } - - log.Log.V(2).Info("mellanox-plugin: configFW()", "cmd-args", cmdArgs) - if len(cmdArgs) <= 4 { - continue - } - _, err := utils.RunCommand("mstconfig", cmdArgs...) - if err != nil { - log.Log.Error(err, "mellanox-plugin configFW(): failed") - return err - } - } - return nil -} - -func getMlnxNicFwData(pciAddress string) (current, next *mlnxNic, err error) { - log.Log.Info("mellanox-plugin getMlnxNicFwData()", "device", pciAddress) - err = nil - attrs := []string{TotalVfs, EnableSriov, LinkTypeP1, LinkTypeP2} - - out, err := utils.MstConfigReadData(pciAddress) - if err != nil { - log.Log.Error(err, "mellanox-plugin getMlnxNicFwData(): failed") - return - } - mstCurrentData, mstNextData := utils.ParseMstconfigOutput(out, attrs) - current, err = mlnxNicFromMap(mstCurrentData) - if err != nil { - log.Log.Error(err, "mellanox-plugin mlnxNicFromMap() for current mstconfig data failed") - return - } - next, err = mlnxNicFromMap(mstNextData) - if err != nil { - log.Log.Error(err, "mellanox-plugin mlnxNicFromMap() for next mstconfig data failed") - } - return -} - -func mlnxNicFromMap(mstData map[string]string) (*mlnxNic, error) { - log.Log.Info("mellanox-plugin mlnxNicFromMap()", "data", mstData) - fwData := &mlnxNic{} - if strings.Contains(mstData[EnableSriov], "True") { - fwData.enableSriov = true - } - i, err := strconv.Atoi(mstData[TotalVfs]) - if err != nil { - return nil, err - } - - fwData.totalVfs = i - fwData.linkTypeP1 = getLinkType(mstData[LinkTypeP1]) - if linkTypeP2, ok := mstData[LinkTypeP2]; ok { - fwData.linkTypeP2 = getLinkType(linkTypeP2) - } - - return fwData, nil -} - -func getPciAddressPrefix(pciAddress string) string { - return pciAddress[:len(pciAddress)-1] -} - -func isDualPort(pciAddress string) bool { - log.Log.Info("mellanox-plugin isDualPort()", "address", pciAddress) - pciAddressPrefix := getPciAddressPrefix(pciAddress) - return len(mellanoxNicsStatus[pciAddressPrefix]) > 1 -} - -func getLinkType(linkType string) string { - log.Log.Info("mellanox-plugin getLinkType()", "link-type", linkType) - if strings.Contains(linkType, constants.LinkTypeETH) { - return constants.LinkTypeETH - } else if strings.Contains(linkType, constants.LinkTypeIB) { - return constants.LinkTypeIB - } else if len(linkType) > 0 { - log.Log.Error(nil, "mellanox-plugin getLinkType(): link type is not one of [ETH, IB], treating as unknown", - "link-type", linkType) - return UknownLinkType - } else { - log.Log.Info("mellanox-plugin getLinkType(): LINK_TYPE_P* attribute was not found, treating as preconfigured link type") - return PreconfiguredLinkType - } -} - -func isLinkTypeRequireChange(iface sriovnetworkv1.Interface, ifaceStatus sriovnetworkv1.InterfaceExt, fwLinkType string) (bool, error) { - log.Log.Info("mellanox-plugin isLinkTypeRequireChange()", "device", iface.PciAddress) - if iface.LinkType != "" && !strings.EqualFold(ifaceStatus.LinkType, iface.LinkType) { - if !strings.EqualFold(iface.LinkType, constants.LinkTypeETH) && !strings.EqualFold(iface.LinkType, constants.LinkTypeIB) { - return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Not supported link type: %s,"+ - " supported link types: [eth, ETH, ib, and IB]", iface.LinkType) - } - if fwLinkType == UknownLinkType { - return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Unknown link type: %s", fwLinkType) - } - if fwLinkType == PreconfiguredLinkType { - return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Network card %s does not support link type change", iface.PciAddress) - } - - return true, nil - } - - return false, nil -} - -func getOtherPortSpec(pciAddress string) *sriovnetworkv1.Interface { - log.Log.Info("mellanox-plugin getOtherPortSpec()", "pciAddress", pciAddress) - pciAddrPrefix := getPciAddressPrefix(pciAddress) - pciAddrSuffix := pciAddress[len(pciAddrPrefix):] - - if pciAddrSuffix == "0" { - iface := mellanoxNicsSpec[pciAddrPrefix+"1"] - return &iface - } - - iface := mellanoxNicsSpec[pciAddrPrefix+"0"] - return &iface -} - -// handleTotalVfs return required total VFs or max (required VFs for dual port NIC) and needReboot if totalVfs will change -func handleTotalVfs(fwCurrent, fwNext, attrs *mlnxNic, ifaceSpec sriovnetworkv1.Interface, isDualPort bool) ( - totalVfs int, needReboot, changeWithoutReboot bool) { - totalVfs = ifaceSpec.NumVfs - // Check if the other port is changing the number of VF - if isDualPort { - otherIfaceSpec := getOtherPortSpec(ifaceSpec.PciAddress) - if otherIfaceSpec != nil { - if otherIfaceSpec.NumVfs > totalVfs { - totalVfs = otherIfaceSpec.NumVfs - } - } - } - - // if the PF is externally managed we just need to check the totalVfs requested in the policy is not higher than - // the configured amount - if ifaceSpec.ExternallyManaged { - if totalVfs > fwCurrent.totalVfs { - log.Log.Error(nil, "The nic is externallyManaged and TotalVfs configured on the system is lower then requested VFs, failing configuration", - "current", fwCurrent.totalVfs, "requested", totalVfs) - attrs.totalVfs = totalVfs - needReboot = true - changeWithoutReboot = false - } - return - } - - if fwCurrent.totalVfs != totalVfs { - log.Log.V(2).Info("Changing TotalVfs, needs reboot", "current", fwCurrent.totalVfs, "requested", totalVfs) - attrs.totalVfs = totalVfs - needReboot = true - } - - // Remove policy then re-apply it - if !needReboot && fwNext.totalVfs != totalVfs { - log.Log.V(2).Info("Changing TotalVfs to same as Next Boot value, doesn't require rebooting", - "current", fwCurrent.totalVfs, "next", fwNext.totalVfs, "requested", totalVfs) - attrs.totalVfs = totalVfs - changeWithoutReboot = true - } - - return -} - -// handleEnableSriov based on totalVfs it decide to disable (totalVfs=0) or enable (totalVfs changed from 0) sriov -// and need reboot if enableSriov will change -func handleEnableSriov(totalVfs int, fwCurrent, fwNext, attrs *mlnxNic) (needReboot, changeWithoutReboot bool) { - if totalVfs == 0 && fwCurrent.enableSriov { - log.Log.V(2).Info("disabling Sriov, needs reboot") - attrs.enableSriov = false - return true, false - } else if totalVfs > 0 && !fwCurrent.enableSriov { - log.Log.V(2).Info("enabling Sriov, needs reboot") - attrs.enableSriov = true - return true, false - } else if totalVfs > 0 && !fwNext.enableSriov { - attrs.enableSriov = true - return false, true - } - - return false, false + return p.helpers.MlxConfigFW(attributesToChange) } -func getIfaceStatus(pciAddress string) sriovnetworkv1.InterfaceExt { - return mellanoxNicsStatus[getPciAddressPrefix(pciAddress)][pciAddress] -} - -// handleLinkType based on existing linkType and requested link -func handleLinkType(pciPrefix string, fwData, attr *mlnxNic) (bool, error) { - needReboot := false - - pciAddress := pciPrefix + "0" - if firstPortSpec, ok := mellanoxNicsSpec[pciAddress]; ok { - ifaceStatus := getIfaceStatus(pciAddress) - needChange, err := isLinkTypeRequireChange(firstPortSpec, ifaceStatus, fwData.linkTypeP1) +// nicHasExternallyManagedPFs returns true if one of the ports(interface) of the NIC is marked as externally managed +// in StoreManagerInterface. +func (p *MellanoxPlugin) nicHasExternallyManagedPFs(nicPortsMap map[string]sriovnetworkv1.InterfaceExt) bool { + for _, iface := range nicPortsMap { + pfStatus, exist, err := p.helpers.LoadPfsStatus(iface.PciAddress) if err != nil { - return false, err - } - - if needChange { - log.Log.V(2).Info("Changing LinkTypeP1, needs reboot", - "from", fwData.linkTypeP1, "to", firstPortSpec.LinkType) - attr.linkTypeP1 = firstPortSpec.LinkType - needReboot = true + log.Log.Error(err, "failed to load PF status from disk", "address", iface.PciAddress) + continue } - } - - pciAddress = pciPrefix + "1" - if secondPortSpec, ok := mellanoxNicsSpec[pciAddress]; ok { - ifaceStatus := getIfaceStatus(pciAddress) - needChange, err := isLinkTypeRequireChange(secondPortSpec, ifaceStatus, fwData.linkTypeP2) - if err != nil { - return false, err + if !exist { + continue } - - if needChange { - log.Log.V(2).Info("Changing LinkTypeP2, needs reboot", - "from", fwData.linkTypeP2, "to", secondPortSpec.LinkType) - attr.linkTypeP2 = secondPortSpec.LinkType - needReboot = true + if pfStatus.ExternallyManaged { + log.Log.V(2).Info("PF is extenally managed, skip FW TotalVfs reset") + return true } } - - return needReboot, nil + return false } diff --git a/pkg/plugins/mock/mock_plugin.go b/pkg/plugins/mock/mock_plugin.go new file mode 100644 index 000000000..b821139c5 --- /dev/null +++ b/pkg/plugins/mock/mock_plugin.go @@ -0,0 +1,93 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: plugin.go + +// Package mock_plugin is a generated GoMock package. +package mock_plugin + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" +) + +// MockVendorPlugin is a mock of VendorPlugin interface. +type MockVendorPlugin struct { + ctrl *gomock.Controller + recorder *MockVendorPluginMockRecorder +} + +// MockVendorPluginMockRecorder is the mock recorder for MockVendorPlugin. +type MockVendorPluginMockRecorder struct { + mock *MockVendorPlugin +} + +// NewMockVendorPlugin creates a new mock instance. +func NewMockVendorPlugin(ctrl *gomock.Controller) *MockVendorPlugin { + mock := &MockVendorPlugin{ctrl: ctrl} + mock.recorder = &MockVendorPluginMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVendorPlugin) EXPECT() *MockVendorPluginMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockVendorPlugin) Apply() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Apply") + ret0, _ := ret[0].(error) + return ret0 +} + +// Apply indicates an expected call of Apply. +func (mr *MockVendorPluginMockRecorder) Apply() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockVendorPlugin)(nil).Apply)) +} + +// Name mocks base method. +func (m *MockVendorPlugin) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockVendorPluginMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockVendorPlugin)(nil).Name)) +} + +// OnNodeStateChange mocks base method. +func (m *MockVendorPlugin) OnNodeStateChange(new *v1.SriovNetworkNodeState) (bool, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnNodeStateChange", new) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// OnNodeStateChange indicates an expected call of OnNodeStateChange. +func (mr *MockVendorPluginMockRecorder) OnNodeStateChange(new interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnNodeStateChange", reflect.TypeOf((*MockVendorPlugin)(nil).OnNodeStateChange), new) +} + +// Spec mocks base method. +func (m *MockVendorPlugin) Spec() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Spec") + ret0, _ := ret[0].(string) + return ret0 +} + +// Spec indicates an expected call of Spec. +func (mr *MockVendorPluginMockRecorder) Spec() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Spec", reflect.TypeOf((*MockVendorPlugin)(nil).Spec)) +} diff --git a/pkg/plugins/plugin.go b/pkg/plugins/plugin.go index 1723ca610..276e37cf2 100644 --- a/pkg/plugins/plugin.go +++ b/pkg/plugins/plugin.go @@ -4,6 +4,7 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" ) +//go:generate ../../bin/mockgen -destination mock/mock_plugin.go -source plugin.go type VendorPlugin interface { // Return the name of plugin Name() string diff --git a/pkg/plugins/virtual/virtual_plugin.go b/pkg/plugins/virtual/virtual_plugin.go index 9fb6cb774..06a57a80f 100644 --- a/pkg/plugins/virtual/virtual_plugin.go +++ b/pkg/plugins/virtual/virtual_plugin.go @@ -6,10 +6,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" + consts "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper" plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) var PluginName = "virtual_plugin" @@ -21,8 +21,7 @@ type VirtualPlugin struct { DesireState *sriovnetworkv1.SriovNetworkNodeState LastState *sriovnetworkv1.SriovNetworkNodeState LoadVfioDriver uint - RunningOnHost bool - HostManager host.HostManagerInterface + helpers helper.HostHelpersInterface } const ( @@ -32,13 +31,12 @@ const ( ) // Initialize our plugin and set up initial values -func NewVirtualPlugin(runningOnHost bool) (plugin.VendorPlugin, error) { +func NewVirtualPlugin(helper helper.HostHelpersInterface) (plugin.VendorPlugin, error) { return &VirtualPlugin{ PluginName: PluginName, SpecVersion: "1.0", LoadVfioDriver: unloaded, - RunningOnHost: runningOnHost, - HostManager: host.NewHostManager(runningOnHost), + helpers: helper, }, nil } @@ -79,12 +77,12 @@ func (p *VirtualPlugin) Apply() error { // This is the case for OpenStack deployments where the underlying virtualization platform is KVM. // NOTE: if VFIO was already loaded for some reason, we will not try to load it again with the new options. kernelArgs := "enable_unsafe_noiommu_mode=1" - if err := p.HostManager.LoadKernelModule("vfio", kernelArgs); err != nil { + if err := p.helpers.LoadKernelModule("vfio", kernelArgs); err != nil { log.Log.Error(err, "virtual-plugin Apply(): fail to load vfio kmod") return err } - if err := p.HostManager.LoadKernelModule("vfio_pci"); err != nil { + if err := p.helpers.LoadKernelModule("vfio_pci"); err != nil { log.Log.Error(err, "virtual-plugin Apply(): fail to load vfio_pci kmod") return err } @@ -98,12 +96,12 @@ func (p *VirtualPlugin) Apply() error { return nil } } - exit, err := utils.Chroot("/host") + exit, err := p.helpers.Chroot(consts.Host) if err != nil { return err } defer exit() - if err := utils.SyncNodeStateVirtual(p.DesireState); err != nil { + if err := syncNodeStateVirtual(p.DesireState, p.helpers); err != nil { return err } p.LastState = &sriovnetworkv1.SriovNetworkNodeState{} @@ -122,7 +120,62 @@ func (p *VirtualPlugin) IsSystemService() bool { func needVfioDriver(state *sriovnetworkv1.SriovNetworkNodeState) bool { for _, iface := range state.Spec.Interfaces { for i := range iface.VfGroups { - if iface.VfGroups[i].DeviceType == constants.DeviceTypeVfioPci { + if iface.VfGroups[i].DeviceType == consts.DeviceTypeVfioPci { + return true + } + } + } + return false +} + +// syncNodeStateVirtual attempt to update the node state to match the desired state in virtual platforms +func syncNodeStateVirtual(newState *sriovnetworkv1.SriovNetworkNodeState, helpers helper.HostHelpersInterface) error { + var err error + for _, ifaceStatus := range newState.Status.Interfaces { + for _, iface := range newState.Spec.Interfaces { + if iface.PciAddress == ifaceStatus.PciAddress { + if !needUpdateVirtual(&iface, &ifaceStatus) { + log.Log.V(2).Info("syncNodeStateVirtual(): no need update interface", "address", iface.PciAddress) + break + } + if err = helpers.ConfigSriovDeviceVirtual(&iface); err != nil { + log.Log.Error(err, "syncNodeStateVirtual(): fail to config sriov interface", "address", iface.PciAddress) + return err + } + break + } + } + } + return nil +} + +func needUpdateVirtual(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) bool { + // The device MTU is set by the platform + // The NumVfs is always 1 + if iface.NumVfs > 0 { + for _, vf := range ifaceStatus.VFs { + ingroup := false + for _, group := range iface.VfGroups { + if sriovnetworkv1.IndexInRange(vf.VfID, group.VfRange) { + ingroup = true + if group.DeviceType != consts.DeviceTypeNetDevice { + if group.DeviceType != vf.Driver { + log.Log.V(2).Info("needUpdateVirtual(): Driver needs update", + "desired", group.DeviceType, "current", vf.Driver) + return true + } + } else { + if sriovnetworkv1.StringInArray(vf.Driver, vars.DpdkDrivers) { + log.Log.V(2).Info("needUpdateVirtual(): Driver needs update", + "desired", group.DeviceType, "current", vf.Driver) + return true + } + } + break + } + } + if !ingroup && sriovnetworkv1.StringInArray(vf.Driver, vars.DpdkDrivers) { + // VF which has DPDK driver loaded but not in any group, needs to be reset to default driver. return true } } diff --git a/pkg/service/service.go b/pkg/service/service.go deleted file mode 100644 index 671d2e20a..000000000 --- a/pkg/service/service.go +++ /dev/null @@ -1,15 +0,0 @@ -package service - -type Service struct { - Name string - Path string - Content string -} - -func NewService(name, path, content string) *Service { - return &Service{ - Name: name, - Path: path, - Content: content, - } -} diff --git a/pkg/service/service_manager.go b/pkg/service/service_manager.go deleted file mode 100644 index 29653fe10..000000000 --- a/pkg/service/service_manager.go +++ /dev/null @@ -1,94 +0,0 @@ -package service - -import ( - "os" - "os/exec" - "path" - "path/filepath" - - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" -) - -type ServiceManager interface { - IsServiceExist(string) (bool, error) - IsServiceEnabled(string) (bool, error) - ReadService(string) (*Service, error) - EnableService(service *Service) error -} - -type serviceManager struct { - chroot string -} - -func NewServiceManager(chroot string) ServiceManager { - root := chroot - if root == "" { - root = "/" - } - return &serviceManager{root} -} - -// IsServiceExist check if service unit exist -func (sm *serviceManager) IsServiceExist(servicePath string) (bool, error) { - _, err := os.Stat(path.Join(sm.chroot, servicePath)) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - return true, nil -} - -// IsServiceEnabled check if service exist and enabled -func (sm *serviceManager) IsServiceEnabled(servicePath string) (bool, error) { - exist, err := sm.IsServiceExist(servicePath) - if err != nil || !exist { - return false, err - } - serviceName := filepath.Base(servicePath) - // Change root dir - exit, err := utils.Chroot(sm.chroot) - if err != nil { - return false, err - } - defer exit() - - cmd := exec.Command("systemctl", "is-enabled", serviceName) - return cmd.Run() == nil, nil -} - -// ReadService read service from given path -func (sm *serviceManager) ReadService(servicePath string) (*Service, error) { - data, err := os.ReadFile(path.Join(sm.chroot, servicePath)) - if err != nil { - return nil, err - } - - return &Service{ - Name: filepath.Base(servicePath), - Path: servicePath, - Content: string(data), - }, nil -} - -// EnableService creates service file and enables it with systemctl enable -func (sm *serviceManager) EnableService(service *Service) error { - // Write service file - err := os.WriteFile(path.Join(sm.chroot, service.Path), []byte(service.Content), 0644) - if err != nil { - return err - } - - // Change root dir - exit, err := utils.Chroot(sm.chroot) - if err != nil { - return err - } - defer exit() - - // Enable service - cmd := exec.Command("systemctl", "enable", service.Name) - return cmd.Run() -} diff --git a/pkg/service/utils.go b/pkg/service/utils.go deleted file mode 100644 index 4abeae553..000000000 --- a/pkg/service/utils.go +++ /dev/null @@ -1,151 +0,0 @@ -package service - -import ( - "io" - "os" - "strings" - - "github.com/coreos/go-systemd/v22/unit" - "gopkg.in/yaml.v2" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -const systemdDir = "/usr/lib/systemd/system/" - -// CompareServices compare 2 service and return true if serviceA has all the fields of serviceB -func CompareServices(serviceA, serviceB *Service) (bool, error) { - optsA, err := unit.Deserialize(strings.NewReader(serviceA.Content)) - if err != nil { - return false, err - } - optsB, err := unit.Deserialize(strings.NewReader(serviceB.Content)) - if err != nil { - return false, err - } - -OUTER: - for _, optB := range optsB { - for _, optA := range optsA { - if optA.Match(optB) { - continue OUTER - } - } - log.Log.V(2).Info("CompareServices", "ServiceA", optsA, "ServiceB", *optB) - return true, nil - } - - return false, nil -} - -// RemoveFromService removes given fields from service -func RemoveFromService(service *Service, options ...*unit.UnitOption) (*Service, error) { - opts, err := unit.Deserialize(strings.NewReader(service.Content)) - if err != nil { - return nil, err - } - - var newServiceOptions []*unit.UnitOption -OUTER: - for _, opt := range opts { - for _, optRemove := range options { - if opt.Match(optRemove) { - continue OUTER - } - } - - newServiceOptions = append(newServiceOptions, opt) - } - - data, err := io.ReadAll(unit.Serialize(newServiceOptions)) - if err != nil { - return nil, err - } - - return &Service{ - Name: service.Name, - Path: service.Path, - Content: string(data), - }, nil -} - -// AppendToService appends given fields to service -func AppendToService(service *Service, options ...*unit.UnitOption) (*Service, error) { - serviceOptions, err := unit.Deserialize(strings.NewReader(service.Content)) - if err != nil { - return nil, err - } - -OUTER: - for _, appendOpt := range options { - for _, opt := range serviceOptions { - if opt.Match(appendOpt) { - continue OUTER - } - } - serviceOptions = append(serviceOptions, appendOpt) - } - - data, err := io.ReadAll(unit.Serialize(serviceOptions)) - if err != nil { - return nil, err - } - - return &Service{ - Name: service.Name, - Path: service.Path, - Content: string(data), - }, nil -} - -// ReadServiceInjectionManifestFile reads service injection file -func ReadServiceInjectionManifestFile(path string) (*Service, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - - var serviceContent ServiceInjectionManifestFile - if err := yaml.Unmarshal(data, &serviceContent); err != nil { - return nil, err - } - - return &Service{ - Name: serviceContent.Name, - Path: systemdDir + serviceContent.Name, - Content: serviceContent.Dropins[0].Contents, - }, nil -} - -// ReadServiceManifestFile reads service file -func ReadServiceManifestFile(path string) (*Service, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - - var serviceFile *ServiceManifestFile - if err := yaml.Unmarshal(data, &serviceFile); err != nil { - return nil, err - } - - return &Service{ - Name: serviceFile.Name, - Path: "/etc/systemd/system/" + serviceFile.Name, - Content: serviceFile.Contents, - }, nil -} - -// ReadScriptManifestFile reads script file -func ReadScriptManifestFile(path string) (*ScriptManifestFile, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - - var scriptFile *ScriptManifestFile - if err := yaml.Unmarshal(data, &scriptFile); err != nil { - return nil, err - } - - return scriptFile, nil -} diff --git a/pkg/systemd/systemd.go b/pkg/systemd/systemd.go index 87ae19a11..f682d85f5 100644 --- a/pkg/systemd/systemd.go +++ b/pkg/systemd/systemd.go @@ -25,30 +25,33 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) const ( - SriovSystemdConfigPath = utils.SriovConfBasePath + "/sriov-interface-config.yaml" - SriovSystemdResultPath = utils.SriovConfBasePath + "/sriov-interface-result.yaml" - sriovSystemdSupportedNicPath = utils.SriovConfBasePath + "/sriov-supported-nics-ids.yaml" + SriovSystemdConfigPath = consts.SriovConfBasePath + "/sriov-interface-config.yaml" + SriovSystemdResultPath = consts.SriovConfBasePath + "/sriov-interface-result.yaml" + sriovSystemdSupportedNicPath = consts.SriovConfBasePath + "/sriov-supported-nics-ids.yaml" sriovSystemdServiceBinaryPath = "/var/lib/sriov/sriov-network-config-daemon" - SriovHostSystemdConfigPath = "/host" + SriovSystemdConfigPath - SriovHostSystemdResultPath = "/host" + SriovSystemdResultPath - sriovHostSystemdSupportedNicPath = "/host" + sriovSystemdSupportedNicPath - sriovHostSystemdServiceBinaryPath = "/host" + sriovSystemdServiceBinaryPath + SriovHostSystemdConfigPath = consts.Host + SriovSystemdConfigPath + SriovHostSystemdResultPath = consts.Host + SriovSystemdResultPath + sriovHostSystemdSupportedNicPath = consts.Host + sriovSystemdSupportedNicPath + sriovHostSystemdServiceBinaryPath = consts.Host + sriovSystemdServiceBinaryPath SriovServicePath = "/etc/systemd/system/sriov-config.service" - SriovHostServicePath = "/host" + SriovServicePath + SriovHostServicePath = consts.Host + SriovServicePath - HostSriovConfBasePath = "/host" + utils.SriovConfBasePath + HostSriovConfBasePath = consts.Host + consts.SriovConfBasePath ) +// TODO: move this to the host interface also + type SriovConfig struct { Spec sriovnetworkv1.SriovNetworkNodeStateSpec `yaml:"spec"` UnsupportedNics bool `yaml:"unsupportedNics"` - PlatformType utils.PlatformType `yaml:"platformType"` + PlatformType consts.PlatformTypes `yaml:"platformType"` } type SriovResult struct { @@ -67,15 +70,15 @@ func ReadConfFile() (spec *SriovConfig, err error) { return spec, err } -func WriteConfFile(newState *sriovnetworkv1.SriovNetworkNodeState, unsupportedNics bool, platformType utils.PlatformType) (bool, error) { +func WriteConfFile(newState *sriovnetworkv1.SriovNetworkNodeState) (bool, error) { newFile := false // remove the device plugin revision as we don't need it here newState.Spec.DpConfigVersion = "" sriovConfig := &SriovConfig{ newState.Spec, - unsupportedNics, - platformType, + vars.DevMode, + vars.PlatformType, } _, err := os.Stat(SriovHostSystemdConfigPath) diff --git a/pkg/utils/cluster.go b/pkg/utils/cluster.go index 610bab415..13310347f 100644 --- a/pkg/utils/cluster.go +++ b/pkg/utils/cluster.go @@ -11,6 +11,8 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" ) const ( @@ -35,7 +37,7 @@ func getNodeRole(node corev1.Node) string { } func IsSingleNodeCluster(c client.Client) (bool, error) { - if os.Getenv("CLUSTER_TYPE") == ClusterTypeOpenshift { + if os.Getenv("CLUSTER_TYPE") == consts.ClusterTypeOpenshift { topo, err := openshiftControlPlaneTopologyStatus(c) if err != nil { return false, err @@ -53,7 +55,7 @@ func IsSingleNodeCluster(c client.Client) (bool, error) { // On kubernetes, it is determined by which node the sriov operator is scheduled on. If operator // pod is schedule on worker node, it is considered as external control plane. func IsExternalControlPlaneCluster(c client.Client) (bool, error) { - if os.Getenv("CLUSTER_TYPE") == ClusterTypeOpenshift { + if os.Getenv("CLUSTER_TYPE") == consts.ClusterTypeOpenshift { topo, err := openshiftControlPlaneTopologyStatus(c) if err != nil { return false, err @@ -61,7 +63,7 @@ func IsExternalControlPlaneCluster(c client.Client) (bool, error) { if topo == "External" { return true, nil } - } else if os.Getenv("CLUSTER_TYPE") == ClusterTypeKubernetes { + } else if os.Getenv("CLUSTER_TYPE") == consts.ClusterTypeKubernetes { role, err := operatorNodeRole(c) if err != nil { return false, err diff --git a/pkg/utils/command.go b/pkg/utils/command.go deleted file mode 100644 index 085cf5881..000000000 --- a/pkg/utils/command.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2021. - -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 utils - -import ( - "bytes" - "os/exec" -) - -// Interface to run commands -// -//go:generate ../../bin/mockgen -destination mock/mock_command.go -source command.go -type CommandInterface interface { - Run(string, ...string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) -} - -type Command struct { -} - -func (c *Command) Run(name string, args ...string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) { - var stdoutbuff, stderrbuff bytes.Buffer - cmd := exec.Command(name, args...) - cmd.Stdout = &stdoutbuff - cmd.Stderr = &stderrbuff - - err = cmd.Run() - return -} diff --git a/pkg/utils/driver.go b/pkg/utils/driver.go deleted file mode 100644 index c91c6448e..000000000 --- a/pkg/utils/driver.go +++ /dev/null @@ -1,119 +0,0 @@ -package utils - -import ( - "fmt" - "os" - "path/filepath" - - dputils "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" - "sigs.k8s.io/controller-runtime/pkg/log" - - sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" -) - -var DpdkDrivers = []string{"igb_uio", "vfio-pci", "uio_pci_generic"} - -// Unbind unbind driver for one device -func Unbind(pciAddr string) error { - log.Log.V(2).Info("Unbind(): unbind device driver for device", "device", pciAddr) - yes, driver := hasDriver(pciAddr) - if !yes { - return nil - } - - filePath := filepath.Join(sysBusPciDrivers, driver, "unbind") - err := os.WriteFile(filePath, []byte(pciAddr), os.ModeAppend) - if err != nil { - log.Log.Error(err, "Unbind(): fail to unbind driver for device", "device", pciAddr) - return err - } - return nil -} - -// BindDpdkDriver bind dpdk driver for one device -// Bind the device given by "pciAddr" to the driver "driver" -func BindDpdkDriver(pciAddr, driver string) error { - log.Log.V(2).Info("BindDpdkDriver(): bind device to driver", - "device", pciAddr, "driver", driver) - - if yes, d := hasDriver(pciAddr); yes { - if driver == d { - log.Log.V(2).Info("BindDpdkDriver(): device already bound to driver", - "device", pciAddr, "driver", driver) - return nil - } - - if err := Unbind(pciAddr); err != nil { - return err - } - } - - driverOverridePath := filepath.Join(sysBusPciDevices, pciAddr, "driver_override") - err := os.WriteFile(driverOverridePath, []byte(driver), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): fail to write driver_override for device", - "device", pciAddr, "driver", driver) - return err - } - bindPath := filepath.Join(sysBusPciDrivers, driver, "bind") - err = os.WriteFile(bindPath, []byte(pciAddr), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): fail to bind driver for device", - "driver", driver, "device", pciAddr) - _, err := os.Readlink(filepath.Join(sysBusPciDevices, pciAddr, "iommu_group")) - if err != nil { - log.Log.Error(err, "Could not read IOMMU group for device", "device", pciAddr) - return fmt.Errorf( - "cannot bind driver %s to device %s, make sure IOMMU is enabled in BIOS. %w", driver, pciAddr, err) - } - return err - } - err = os.WriteFile(driverOverridePath, []byte(""), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): failed to clear driver_override for device", "device", pciAddr) - return err - } - - return nil -} - -// BindDefaultDriver bind driver for one device -// Bind the device given by "pciAddr" to the default driver -func BindDefaultDriver(pciAddr string) error { - log.Log.V(2).Info("BindDefaultDriver(): bind device to default driver", "device", pciAddr) - - if yes, d := hasDriver(pciAddr); yes { - if !sriovnetworkv1.StringInArray(d, DpdkDrivers) { - log.Log.V(2).Info("BindDefaultDriver(): device already bound to default driver", - "device", pciAddr, "driver", d) - return nil - } - if err := Unbind(pciAddr); err != nil { - return err - } - } - - driverOverridePath := filepath.Join(sysBusPciDevices, pciAddr, "driver_override") - err := os.WriteFile(driverOverridePath, []byte("\x00"), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDefaultDriver(): failed to write driver_override for device", "device", pciAddr) - return err - } - err = os.WriteFile(sysBusPciDriversProbe, []byte(pciAddr), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDefaultDriver(): failed to bind driver for device", "device", pciAddr) - return err - } - - return nil -} - -func hasDriver(pciAddr string) (bool, string) { - driver, err := dputils.GetDriverName(pciAddr) - if err != nil { - log.Log.V(2).Info("hasDriver(): device driver is empty for device", "device", pciAddr) - return false, "" - } - log.Log.V(2).Info("hasDriver(): device driver for device", "device", pciAddr, "driver", driver) - return true, driver -} diff --git a/pkg/utils/mock/mock_utils.go b/pkg/utils/mock/mock_utils.go new file mode 100644 index 000000000..636c7f4a1 --- /dev/null +++ b/pkg/utils/mock/mock_utils.go @@ -0,0 +1,70 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: utils.go + +// Package mock_utils is a generated GoMock package. +package mock_utils + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockCmdInterface is a mock of CmdInterface interface. +type MockCmdInterface struct { + ctrl *gomock.Controller + recorder *MockCmdInterfaceMockRecorder +} + +// MockCmdInterfaceMockRecorder is the mock recorder for MockCmdInterface. +type MockCmdInterfaceMockRecorder struct { + mock *MockCmdInterface +} + +// NewMockCmdInterface creates a new mock instance. +func NewMockCmdInterface(ctrl *gomock.Controller) *MockCmdInterface { + mock := &MockCmdInterface{ctrl: ctrl} + mock.recorder = &MockCmdInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCmdInterface) EXPECT() *MockCmdInterfaceMockRecorder { + return m.recorder +} + +// Chroot mocks base method. +func (m *MockCmdInterface) Chroot(arg0 string) (func() error, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Chroot", arg0) + ret0, _ := ret[0].(func() error) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Chroot indicates an expected call of Chroot. +func (mr *MockCmdInterfaceMockRecorder) Chroot(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Chroot", reflect.TypeOf((*MockCmdInterface)(nil).Chroot), arg0) +} + +// RunCommand mocks base method. +func (m *MockCmdInterface) RunCommand(arg0 string, arg1 ...string) (string, string, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RunCommand", varargs...) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// RunCommand indicates an expected call of RunCommand. +func (mr *MockCmdInterfaceMockRecorder) RunCommand(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunCommand", reflect.TypeOf((*MockCmdInterface)(nil).RunCommand), varargs...) +} diff --git a/pkg/utils/shutdown.go b/pkg/utils/shutdown.go index 3700efa18..208e2073c 100644 --- a/pkg/utils/shutdown.go +++ b/pkg/utils/shutdown.go @@ -11,7 +11,7 @@ import ( sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" ) var shutdownLog = ctrl.Log.WithName("shutdown") @@ -68,7 +68,7 @@ func updateWebhooks() { func updateValidatingWebhook(c *kubernetes.Clientset) { validatingWebhookClient := c.AdmissionregistrationV1().ValidatingWebhookConfigurations() - webhook, err := validatingWebhookClient.Get(context.TODO(), constants.OperatorWebHookName, metav1.GetOptions{}) + webhook, err := validatingWebhookClient.Get(context.TODO(), consts.OperatorWebHookName, metav1.GetOptions{}) if err != nil { shutdownLog.Error(err, "Error getting webhook") } @@ -81,7 +81,7 @@ func updateValidatingWebhook(c *kubernetes.Clientset) { func updateMutatingWebhooks(c *kubernetes.Clientset) { mutatingWebhookClient := c.AdmissionregistrationV1().MutatingWebhookConfigurations() - for _, name := range []string{constants.OperatorWebHookName, constants.InjectorWebHookName} { + for _, name := range []string{consts.OperatorWebHookName, consts.InjectorWebHookName} { mutatingWebhook, err := mutatingWebhookClient.Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { shutdownLog.Error(err, "Error getting webhook") diff --git a/pkg/utils/sriov.go b/pkg/utils/sriov.go deleted file mode 100644 index f0668494f..000000000 --- a/pkg/utils/sriov.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2021. - -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 utils - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - - "sigs.k8s.io/controller-runtime/pkg/log" - - sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" -) - -const ( - SriovSwitchDevConfPath = SriovConfBasePath + "/sriov_config.json" - SriovHostSwitchDevConfPath = "/host" + SriovSwitchDevConfPath -) - -type config struct { - Interfaces []sriovnetworkv1.Interface `json:"interfaces"` -} - -func IsSwitchdevModeSpec(spec sriovnetworkv1.SriovNetworkNodeStateSpec) bool { - for _, iface := range spec.Interfaces { - if iface.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { - return true - } - } - return false -} - -func findInterface(interfaces sriovnetworkv1.Interfaces, name string) (iface sriovnetworkv1.Interface, err error) { - for _, i := range interfaces { - if i.Name == name { - return i, nil - } - } - return sriovnetworkv1.Interface{}, fmt.Errorf("unable to find interface: %v", name) -} - -func WriteSwitchdevConfFile(newState *sriovnetworkv1.SriovNetworkNodeState) (update bool, err error) { - // Create a map with all the PFs we will need to SKIP for systemd configuration - pfsToSkip, err := GetPfsToSkip(newState) - if err != nil { - return false, err - } - cfg := config{} - for _, iface := range newState.Spec.Interfaces { - for _, ifaceStatus := range newState.Status.Interfaces { - if iface.PciAddress != ifaceStatus.PciAddress { - continue - } - - if skip := pfsToSkip[iface.PciAddress]; !skip { - continue - } - - i := sriovnetworkv1.Interface{} - if iface.NumVfs > 0 { - var vfGroups []sriovnetworkv1.VfGroup = nil - ifc, err := findInterface(newState.Spec.Interfaces, iface.Name) - if err != nil { - log.Log.Error(err, "WriteSwitchdevConfFile(): fail find interface") - } else { - vfGroups = ifc.VfGroups - } - i = sriovnetworkv1.Interface{ - // Not passing all the contents, since only NumVfs and EswitchMode can be configured by configure-switchdev.sh currently. - Name: iface.Name, - PciAddress: iface.PciAddress, - NumVfs: iface.NumVfs, - Mtu: iface.Mtu, - VfGroups: vfGroups, - } - - if iface.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { - i.EswitchMode = iface.EswitchMode - } - cfg.Interfaces = append(cfg.Interfaces, i) - } - } - } - _, err = os.Stat(SriovHostSwitchDevConfPath) - if err != nil { - if os.IsNotExist(err) { - if len(cfg.Interfaces) == 0 { - err = nil - return - } - - // Create the sriov-operator folder on the host if it doesn't exist - if _, err := os.Stat("/host" + SriovConfBasePath); os.IsNotExist(err) { - err = os.Mkdir("/host"+SriovConfBasePath, os.ModeDir) - if err != nil { - log.Log.Error(err, "WriteConfFile(): failed to create sriov-operator folder") - return false, err - } - } - - log.Log.V(2).Info("WriteSwitchdevConfFile(): file not existed, create it") - _, err = os.Create(SriovHostSwitchDevConfPath) - if err != nil { - log.Log.Error(err, "WriteSwitchdevConfFile(): failed to create file") - return - } - } else { - return - } - } - oldContent, err := os.ReadFile(SriovHostSwitchDevConfPath) - if err != nil { - log.Log.Error(err, "WriteSwitchdevConfFile(): failed to read file") - return - } - var newContent []byte - if len(cfg.Interfaces) != 0 { - newContent, err = json.Marshal(cfg) - if err != nil { - log.Log.Error(err, "WriteSwitchdevConfFile(): fail to marshal config") - return - } - } - - if bytes.Equal(newContent, oldContent) { - log.Log.V(2).Info("WriteSwitchdevConfFile(): no update") - return - } - update = true - log.Log.V(2).Info("WriteSwitchdevConfFile(): write to switchdev.conf", "content", newContent) - err = os.WriteFile(SriovHostSwitchDevConfPath, newContent, 0644) - if err != nil { - log.Log.Error(err, "WriteSwitchdevConfFile(): failed to write file") - return - } - return -} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 9452bce41..3a0bbdc4c 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -2,741 +2,38 @@ package utils import ( "bytes" - "errors" + "encoding/hex" "fmt" + "hash/fnv" "math/rand" "net" "os" "os/exec" - "path" "path/filepath" - "regexp" - "strconv" - "strings" - "syscall" - "time" - - "encoding/hex" - "hash/fnv" "sort" + "syscall" corev1 "k8s.io/api/core/v1" - - "github.com/cenkalti/backoff" - "github.com/jaypipes/ghw" - "github.com/vishvananda/netlink" - "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/log" - dputils "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" - - sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) -const ( - sysBusPciDevices = "/sys/bus/pci/devices" - sysBusPciDrivers = "/sys/bus/pci/drivers" - sysBusPciDriversProbe = "/sys/bus/pci/drivers_probe" - sysClassNet = "/sys/class/net" - procKernelCmdLine = "/proc/cmdline" - netClass = 0x02 - numVfsFile = "sriov_numvfs" - - ClusterTypeOpenshift = "openshift" - ClusterTypeKubernetes = "kubernetes" - VendorMellanox = "15b3" - DeviceBF2 = "a2d6" - DeviceBF3 = "a2dc" - - udevFolder = "/etc/udev" - udevRulesFolder = udevFolder + "/rules.d" - udevDisableNM = "/bindata/scripts/udev-find-sriov-pf.sh" - nmUdevRule = "SUBSYSTEM==\"net\", ACTION==\"add|change|move\", ATTRS{device}==\"%s\", IMPORT{program}=\"/etc/udev/disable-nm-sriov.sh $env{INTERFACE} %s\"" - - KernelArgPciRealloc = "pci=realloc" - KernelArgIntelIommu = "intel_iommu=on" - KernelArgIommuPt = "iommu=pt" -) - -var InitialState sriovnetworkv1.SriovNetworkNodeState -var ClusterType string - -var pfPhysPortNameRe = regexp.MustCompile(`p\d+`) - -// FilesystemRoot used by test to mock interactions with filesystem -var FilesystemRoot = "" - -var SupportedVfIds []string - -func init() { - ClusterType = os.Getenv("CLUSTER_TYPE") -} - -// GetCurrentKernelArgs This retrieves the kernel cmd line arguments -func GetCurrentKernelArgs(chroot bool) (string, error) { - path := procKernelCmdLine - if !chroot { - path = "/host" + path - } - cmdLine, err := os.ReadFile(path) - if err != nil { - return "", fmt.Errorf("GetCurrentKernelArgs(): Error reading %s: %v", procKernelCmdLine, err) - } - return string(cmdLine), nil -} - -// IsKernelArgsSet This checks if the kernel cmd line is set properly. Please note that the same key could be repeated -// several times in the kernel cmd line. We can only ensure that the kernel cmd line has the key/val kernel arg that we set. -func IsKernelArgsSet(cmdLine string, karg string) bool { - elements := strings.Fields(cmdLine) - for _, element := range elements { - if element == karg { - return true - } - } - return false -} - -func DiscoverSriovDevices(withUnsupported bool, storeManager StoreManagerInterface) ([]sriovnetworkv1.InterfaceExt, error) { - log.Log.V(2).Info("DiscoverSriovDevices") - pfList := []sriovnetworkv1.InterfaceExt{} - - pci, err := ghw.PCI() - if err != nil { - return nil, fmt.Errorf("DiscoverSriovDevices(): error getting PCI info: %v", err) - } - - devices := pci.ListDevices() - if len(devices) == 0 { - return nil, fmt.Errorf("DiscoverSriovDevices(): could not retrieve PCI devices") - } - - for _, device := range devices { - devClass, err := strconv.ParseInt(device.Class.ID, 16, 64) - if err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device class, skipping", - "device", device) - continue - } - if devClass != netClass { - // Not network device - continue - } - - // TODO: exclude devices used by host system - - if dputils.IsSriovVF(device.Address) { - continue - } - - driver, err := dputils.GetDriverName(device.Address) - if err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device driver for device, skipping", "device", device) - continue - } - - deviceNames, err := dputils.GetNetNames(device.Address) - if err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): unable to get device names for device, skipping", "device", device) - continue - } - - if len(deviceNames) == 0 { - // no network devices found, skipping device - continue - } - - if !withUnsupported { - if !sriovnetworkv1.IsSupportedModel(device.Vendor.ID, device.Product.ID) { - log.Log.Info("DiscoverSriovDevices(): unsupported device", "device", device) - continue - } - } - - iface := sriovnetworkv1.InterfaceExt{ - PciAddress: device.Address, - Driver: driver, - Vendor: device.Vendor.ID, - DeviceID: device.Product.ID, - } - if mtu := getNetdevMTU(device.Address); mtu > 0 { - iface.Mtu = mtu - } - if name := tryGetInterfaceName(device.Address); name != "" { - iface.Name = name - iface.Mac = getNetDevMac(name) - iface.LinkSpeed = getNetDevLinkSpeed(name) - } - iface.LinkType = getLinkType(iface) - - pfStatus, exist, err := storeManager.LoadPfsStatus(iface.PciAddress) - if err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): failed to load PF status from disk") - } else { - if exist { - iface.ExternallyManaged = pfStatus.ExternallyManaged - } - } - - if dputils.IsSriovPF(device.Address) { - iface.TotalVfs = dputils.GetSriovVFcapacity(device.Address) - iface.NumVfs = dputils.GetVFconfigured(device.Address) - if iface.EswitchMode, err = GetNicSriovMode(device.Address); err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): warning, unable to get device eswitch mode", - "device", device.Address) - } - if dputils.SriovConfigured(device.Address) { - vfs, err := dputils.GetVFList(device.Address) - if err != nil { - log.Log.Error(err, "DiscoverSriovDevices(): unable to parse VFs for device, skipping", - "device", device) - continue - } - for _, vf := range vfs { - instance := getVfInfo(vf, devices) - iface.VFs = append(iface.VFs, instance) - } - } - } - pfList = append(pfList, iface) - } - - return pfList, nil -} - -// SyncNodeState Attempt to update the node state to match the desired state -func SyncNodeState(newState *sriovnetworkv1.SriovNetworkNodeState, pfsToConfig map[string]bool) error { - return ConfigSriovInterfaces(newState.Spec.Interfaces, newState.Status.Interfaces, pfsToConfig) -} - -func ConfigSriovInterfaces(interfaces []sriovnetworkv1.Interface, ifaceStatuses []sriovnetworkv1.InterfaceExt, pfsToConfig map[string]bool) error { - if IsKernelLockdownMode(true) && hasMellanoxInterfacesInSpec(ifaceStatuses, interfaces) { - log.Log.Error(nil, "cannot use mellanox devices when in kernel lockdown mode") - return fmt.Errorf("cannot use mellanox devices when in kernel lockdown mode") - } - - // we are already inside chroot, so we initialize the store as running on host - storeManager, err := NewStoreManager(true) - if err != nil { - return fmt.Errorf("SyncNodeState(): error initializing storeManager: %v", err) - } - - for _, ifaceStatus := range ifaceStatuses { - configured := false - for _, iface := range interfaces { - if iface.PciAddress == ifaceStatus.PciAddress { - configured = true - - if skip := pfsToConfig[iface.PciAddress]; skip { - break - } - - if !NeedUpdate(&iface, &ifaceStatus) { - log.Log.V(2).Info("syncNodeState(): no need update interface", "address", iface.PciAddress) - - // Save the PF status to the host - err = storeManager.SaveLastPfAppliedStatus(&iface) - if err != nil { - log.Log.Error(err, "SyncNodeState(): failed to save PF applied config to host") - return err - } - - break - } - if err = configSriovDevice(&iface, &ifaceStatus); err != nil { - log.Log.Error(err, "SyncNodeState(): fail to configure sriov interface. resetting interface.", "address", iface.PciAddress) - if iface.ExternallyManaged { - log.Log.Info("SyncNodeState(): skipping device reset as the nic is marked as externally created") - } else { - if resetErr := resetSriovDevice(ifaceStatus); resetErr != nil { - log.Log.Error(resetErr, "SyncNodeState(): failed to reset on error SR-IOV interface") - } - } - return err - } - - // Save the PF status to the host - err = storeManager.SaveLastPfAppliedStatus(&iface) - if err != nil { - log.Log.Error(err, "SyncNodeState(): failed to save PF applied config to host") - return err - } - break - } - } - if !configured && ifaceStatus.NumVfs > 0 { - if skip := pfsToConfig[ifaceStatus.PciAddress]; skip { - continue - } - - // load the PF info - pfStatus, exist, err := storeManager.LoadPfsStatus(ifaceStatus.PciAddress) - if err != nil { - log.Log.Error(err, "SyncNodeState(): failed to load info about PF status for device", - "address", ifaceStatus.PciAddress) - return err - } - - if !exist { - log.Log.Info("SyncNodeState(): PF name with pci address has VFs configured but they weren't created by the sriov operator. Skipping the device reset", - "pf-name", ifaceStatus.Name, - "address", ifaceStatus.PciAddress) - continue - } - - if pfStatus.ExternallyManaged { - log.Log.Info("SyncNodeState(): PF name with pci address was externally created skipping the device reset", - "pf-name", ifaceStatus.Name, - "address", ifaceStatus.PciAddress) - continue - } else { - err = RemoveUdevRule(ifaceStatus.PciAddress) - if err != nil { - return err - } - } - - if err = resetSriovDevice(ifaceStatus); err != nil { - return err - } - } - } - return nil -} - -// skipConfigVf Use systemd service to configure switchdev mode or BF-2 NICs in OpenShift -func skipConfigVf(ifSpec sriovnetworkv1.Interface, ifStatus sriovnetworkv1.InterfaceExt) (bool, error) { - if ifSpec.EswitchMode == sriovnetworkv1.ESwithModeSwitchDev { - log.Log.V(2).Info("skipConfigVf(): skip config VF for switchdev device") - return true, nil - } - - // NVIDIA BlueField 2 and BlueField3 in OpenShift - if ClusterType == ClusterTypeOpenshift && ifStatus.Vendor == VendorMellanox && (ifStatus.DeviceID == DeviceBF2 || ifStatus.DeviceID == DeviceBF3) { - // TODO: remove this when switch to the systemd configuration support. - mode, err := mellanoxBlueFieldMode(ifStatus.PciAddress) - if err != nil { - return false, fmt.Errorf("failed to read Mellanox Bluefield card mode for %s,%v", ifStatus.PciAddress, err) - } - - if mode == bluefieldConnectXMode { - return false, nil - } - - log.Log.V(2).Info("skipConfigVf(): skip config VF for Bluefiled card on DPU mode") - return true, nil - } - - return false, nil -} - -// GetPfsToSkip return a map of devices pci addresses to should be configured via systemd instead if the legacy mode -// we skip devices in switchdev mode and Bluefield card in ConnectX mode -func GetPfsToSkip(ns *sriovnetworkv1.SriovNetworkNodeState) (map[string]bool, error) { - pfsToSkip := map[string]bool{} - for _, ifaceStatus := range ns.Status.Interfaces { - for _, iface := range ns.Spec.Interfaces { - if iface.PciAddress == ifaceStatus.PciAddress { - skip, err := skipConfigVf(iface, ifaceStatus) - if err != nil { - log.Log.Error(err, "GetPfsToSkip(): fail to check for skip VFs", "device", iface.PciAddress) - return pfsToSkip, err - } - pfsToSkip[iface.PciAddress] = skip - break - } - } - } - - return pfsToSkip, nil -} - -func NeedUpdate(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) bool { - if iface.Mtu > 0 { - mtu := iface.Mtu - if mtu != ifaceStatus.Mtu { - log.Log.V(2).Info("NeedUpdate(): MTU needs update", "desired", mtu, "current", ifaceStatus.Mtu) - return true - } - } - - if iface.NumVfs != ifaceStatus.NumVfs { - log.Log.V(2).Info("NeedUpdate(): NumVfs needs update", "desired", iface.NumVfs, "current", ifaceStatus.NumVfs) - return true - } - if iface.NumVfs > 0 { - for _, vf := range ifaceStatus.VFs { - ingroup := false - for _, group := range iface.VfGroups { - if sriovnetworkv1.IndexInRange(vf.VfID, group.VfRange) { - ingroup = true - if group.DeviceType != constants.DeviceTypeNetDevice { - if group.DeviceType != vf.Driver { - log.Log.V(2).Info("NeedUpdate(): Driver needs update", - "desired", group.DeviceType, "current", vf.Driver) - return true - } - } else { - if sriovnetworkv1.StringInArray(vf.Driver, DpdkDrivers) { - log.Log.V(2).Info("NeedUpdate(): Driver needs update", - "desired", group.DeviceType, "current", vf.Driver) - return true - } - if vf.Mtu != 0 && group.Mtu != 0 && vf.Mtu != group.Mtu { - log.Log.V(2).Info("NeedUpdate(): VF MTU needs update", - "vf", vf.VfID, "desired", group.Mtu, "current", vf.Mtu) - return true - } - - // this is needed to be sure the admin mac address is configured as expected - if iface.ExternallyManaged { - log.Log.V(2).Info("NeedUpdate(): need to update the device as it's externally manage", - "device", ifaceStatus.PciAddress) - return true - } - } - break - } - } - if !ingroup && sriovnetworkv1.StringInArray(vf.Driver, DpdkDrivers) { - // VF which has DPDK driver loaded but not in any group, needs to be reset to default driver. - return true - } - } - } - return false -} - -func configSriovDevice(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt) error { - log.Log.V(2).Info("configSriovDevice(): configure sriov device", - "device", iface.PciAddress, "config", iface) - var err error - if iface.NumVfs > ifaceStatus.TotalVfs { - err := fmt.Errorf("cannot config SRIOV device: NumVfs (%d) is larger than TotalVfs (%d)", iface.NumVfs, ifaceStatus.TotalVfs) - log.Log.Error(err, "configSriovDevice(): fail to set NumVfs for device", "device", iface.PciAddress) - return err - } - // set numVFs - if iface.NumVfs != ifaceStatus.NumVfs { - if iface.ExternallyManaged { - if iface.NumVfs > ifaceStatus.NumVfs { - errMsg := fmt.Sprintf("configSriovDevice(): number of request virtual functions %d is not equal to configured virtual functions %d but the policy is configured as ExternallyManaged for device %s", iface.NumVfs, ifaceStatus.NumVfs, iface.PciAddress) - log.Log.Error(nil, errMsg) - return fmt.Errorf(errMsg) - } - } else { - // create the udev rule to disable all the vfs from network manager as this vfs are managed by the operator - err = AddUdevRule(iface.PciAddress) - if err != nil { - return err - } - - err = setSriovNumVfs(iface.PciAddress, iface.NumVfs) - if err != nil { - log.Log.Error(err, "configSriovDevice(): fail to set NumVfs for device", "device", iface.PciAddress) - errRemove := RemoveUdevRule(iface.PciAddress) - if errRemove != nil { - log.Log.Error(errRemove, "configSriovDevice(): fail to remove udev rule", "device", iface.PciAddress) - } - return err - } - } - } - // set PF mtu - if iface.Mtu > 0 && iface.Mtu > ifaceStatus.Mtu { - err = setNetdevMTU(iface.PciAddress, iface.Mtu) - if err != nil { - log.Log.Error(err, "configSriovDevice(): fail to set mtu for PF", "device", iface.PciAddress) - return err - } - } - // Config VFs - if iface.NumVfs > 0 { - vfAddrs, err := dputils.GetVFList(iface.PciAddress) - if err != nil { - log.Log.Error(err, "configSriovDevice(): unable to parse VFs for device", "device", iface.PciAddress) - } - pfLink, err := netlink.LinkByName(iface.Name) - if err != nil { - log.Log.Error(err, "configSriovDevice(): unable to get PF link for device", "device", iface) - return err - } - - for _, addr := range vfAddrs { - var group *sriovnetworkv1.VfGroup - - vfID, err := dputils.GetVFID(addr) - if err != nil { - log.Log.Error(err, "configSriovDevice(): unable to get VF id", "device", iface.PciAddress) - return err - } - - for i := range iface.VfGroups { - if sriovnetworkv1.IndexInRange(vfID, iface.VfGroups[i].VfRange) { - group = &iface.VfGroups[i] - break - } - } - - // VF group not found. - if group == nil { - continue - } - - // only set GUID and MAC for VF with default driver - // for userspace drivers like vfio we configure the vf mac using the kernel nic mac address - // before we switch to the userspace driver - if yes, d := hasDriver(addr); yes && !sriovnetworkv1.StringInArray(d, DpdkDrivers) { - // LinkType is an optional field. Let's fallback to current link type - // if nothing is specified in the SriovNodePolicy - linkType := iface.LinkType - if linkType == "" { - linkType = ifaceStatus.LinkType - } - if strings.EqualFold(linkType, constants.LinkTypeIB) { - if err = setVfGUID(addr, pfLink); err != nil { - return err - } - } else { - vfLink, err := vfIsReady(addr) - if err != nil { - log.Log.Error(err, "configSriovDevice(): VF link is not ready", "address", addr) - err = RebindVfToDefaultDriver(addr) - if err != nil { - log.Log.Error(err, "configSriovDevice(): failed to rebind VF", "address", addr) - return err - } - - // Try to check the VF status again - vfLink, err = vfIsReady(addr) - if err != nil { - log.Log.Error(err, "configSriovDevice(): VF link is not ready", "address", addr) - return err - } - } - if err = setVfAdminMac(addr, pfLink, vfLink); err != nil { - log.Log.Error(err, "configSriovDevice(): fail to configure VF admin mac", "device", addr) - return err - } - } - } - - if err = unbindDriverIfNeeded(addr, group.IsRdma); err != nil { - return err - } - - if !sriovnetworkv1.StringInArray(group.DeviceType, DpdkDrivers) { - if err := BindDefaultDriver(addr); err != nil { - log.Log.Error(err, "configSriovDevice(): fail to bind default driver for device", "device", addr) - return err - } - // only set MTU for VF with default driver - if group.Mtu > 0 { - if err := setNetdevMTU(addr, group.Mtu); err != nil { - log.Log.Error(err, "configSriovDevice(): fail to set mtu for VF", "address", addr) - return err - } - } - } else { - if err := BindDpdkDriver(addr, group.DeviceType); err != nil { - log.Log.Error(err, "configSriovDevice(): fail to bind driver for device", - "driver", group.DeviceType, "device", addr) - return err - } - } - } - } - // Set PF link up - pfLink, err := netlink.LinkByName(ifaceStatus.Name) - if err != nil { - return err - } - if pfLink.Attrs().OperState != netlink.OperUp { - err = netlink.LinkSetUp(pfLink) - if err != nil { - return err - } - } - return nil -} - -func setSriovNumVfs(pciAddr string, numVfs int) error { - log.Log.V(2).Info("setSriovNumVfs(): set NumVfs", "device", pciAddr, "numVfs", numVfs) - numVfsFilePath := filepath.Join(sysBusPciDevices, pciAddr, numVfsFile) - bs := []byte(strconv.Itoa(numVfs)) - err := os.WriteFile(numVfsFilePath, []byte("0"), os.ModeAppend) - if err != nil { - log.Log.Error(err, "setSriovNumVfs(): fail to reset NumVfs file", "path", numVfsFilePath) - return err - } - err = os.WriteFile(numVfsFilePath, bs, os.ModeAppend) - if err != nil { - log.Log.Error(err, "setSriovNumVfs(): fail to set NumVfs file", "path", numVfsFilePath) - return err - } - return nil -} - -func setNetdevMTU(pciAddr string, mtu int) error { - log.Log.V(2).Info("setNetdevMTU(): set MTU", "device", pciAddr, "mtu", mtu) - if mtu <= 0 { - log.Log.V(2).Info("setNetdevMTU(): refusing to set MTU", "mtu", mtu) - return nil - } - b := backoff.NewConstantBackOff(1 * time.Second) - err := backoff.Retry(func() error { - ifaceName, err := dputils.GetNetNames(pciAddr) - if err != nil { - log.Log.Error(err, "setNetdevMTU(): fail to get interface name", "device", pciAddr) - return err - } - if len(ifaceName) < 1 { - return fmt.Errorf("setNetdevMTU(): interface name is empty") - } - mtuFile := "net/" + ifaceName[0] + "/mtu" - mtuFilePath := filepath.Join(sysBusPciDevices, pciAddr, mtuFile) - return os.WriteFile(mtuFilePath, []byte(strconv.Itoa(mtu)), os.ModeAppend) - }, backoff.WithMaxRetries(b, 10)) - if err != nil { - log.Log.Error(err, "setNetdevMTU(): fail to write mtu file after retrying") - return err - } - return nil -} - -func tryGetInterfaceName(pciAddr string) string { - names, err := dputils.GetNetNames(pciAddr) - if err != nil || len(names) < 1 { - return "" - } - netDevName := names[0] - - // Switchdev PF and their VFs representors are existing under the same PCI address since kernel 5.8 - // if device is switchdev then return PF name - for _, name := range names { - if !isSwitchdev(name) { - continue - } - // Try to get the phys port name, if not exists then fallback to check without it - // phys_port_name should be in formant p e.g p0,p1,p2 ...etc. - if physPortName, err := GetPhysPortName(name); err == nil { - if !pfPhysPortNameRe.MatchString(physPortName) { - continue - } - } - return name - } - - log.Log.V(2).Info("tryGetInterfaceName()", "name", netDevName) - return netDevName -} - -func getNetdevMTU(pciAddr string) int { - log.Log.V(2).Info("getNetdevMTU(): get MTU", "device", pciAddr) - ifaceName := tryGetInterfaceName(pciAddr) - if ifaceName == "" { - return 0 - } - mtuFile := "net/" + ifaceName + "/mtu" - mtuFilePath := filepath.Join(sysBusPciDevices, pciAddr, mtuFile) - data, err := os.ReadFile(mtuFilePath) - if err != nil { - log.Log.Error(err, "getNetdevMTU(): fail to read mtu file", "path", mtuFilePath) - return 0 - } - mtu, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - log.Log.Error(err, "getNetdevMTU(): fail to convert mtu to int", "raw-mtu", strings.TrimSpace(string(data))) - return 0 - } - return mtu -} - -func getNetDevMac(ifaceName string) string { - log.Log.V(2).Info("getNetDevMac(): get Mac", "device", ifaceName) - macFilePath := filepath.Join(sysClassNet, ifaceName, "address") - data, err := os.ReadFile(macFilePath) - if err != nil { - log.Log.Error(err, "getNetDevMac(): fail to read Mac file", "path", macFilePath) - return "" - } - - return strings.TrimSpace(string(data)) -} - -func getNetDevLinkSpeed(ifaceName string) string { - log.Log.V(2).Info("getNetDevLinkSpeed(): get LinkSpeed", "device", ifaceName) - speedFilePath := filepath.Join(sysClassNet, ifaceName, "speed") - data, err := os.ReadFile(speedFilePath) - if err != nil { - log.Log.Error(err, "getNetDevLinkSpeed(): fail to read Link Speed file", "path", speedFilePath) - return "" - } - - return fmt.Sprintf("%s Mb/s", strings.TrimSpace(string(data))) +//go:generate ../../bin/mockgen -destination mock/mock_utils.go -source utils.go +type CmdInterface interface { + Chroot(string) (func() error, error) + RunCommand(string, ...string) (string, string, error) } -func resetSriovDevice(ifaceStatus sriovnetworkv1.InterfaceExt) error { - log.Log.V(2).Info("resetSriovDevice(): reset SRIOV device", "address", ifaceStatus.PciAddress) - if err := setSriovNumVfs(ifaceStatus.PciAddress, 0); err != nil { - return err - } - if ifaceStatus.LinkType == constants.LinkTypeETH { - var mtu int - is := InitialState.GetInterfaceStateByPciAddress(ifaceStatus.PciAddress) - if is != nil { - mtu = is.Mtu - } else { - mtu = 1500 - } - log.Log.V(2).Info("resetSriovDevice(): reset mtu", "value", mtu) - if err := setNetdevMTU(ifaceStatus.PciAddress, mtu); err != nil { - return err - } - } else if ifaceStatus.LinkType == constants.LinkTypeIB { - if err := setNetdevMTU(ifaceStatus.PciAddress, 2048); err != nil { - return err - } - } - return nil +type utilsHelper struct { } -func getVfInfo(pciAddr string, devices []*ghw.PCIDevice) sriovnetworkv1.VirtualFunction { - driver, err := dputils.GetDriverName(pciAddr) - if err != nil { - log.Log.Error(err, "getVfInfo(): unable to parse device driver", "device", pciAddr) - } - id, err := dputils.GetVFID(pciAddr) - if err != nil { - log.Log.Error(err, "getVfInfo(): unable to get VF index", "device", pciAddr) - } - vf := sriovnetworkv1.VirtualFunction{ - PciAddress: pciAddr, - Driver: driver, - VfID: id, - } - - if mtu := getNetdevMTU(pciAddr); mtu > 0 { - vf.Mtu = mtu - } - if name := tryGetInterfaceName(pciAddr); name != "" { - vf.Name = name - vf.Mac = getNetDevMac(name) - } - - for _, device := range devices { - if pciAddr == device.Address { - vf.Vendor = device.Vendor.ID - vf.DeviceID = device.Product.ID - break - } - continue - } - return vf +func New() CmdInterface { + return &utilsHelper{} } -func Chroot(path string) (func() error, error) { +func (u *utilsHelper) Chroot(path string) (func() error, error) { root, err := os.Open("/") if err != nil { return nil, err @@ -746,101 +43,33 @@ func Chroot(path string) (func() error, error) { root.Close() return nil, err } + vars.InChroot = true return func() error { defer root.Close() if err := root.Chdir(); err != nil { return err } + vars.InChroot = false return syscall.Chroot(".") }, nil } -func vfIsReady(pciAddr string) (netlink.Link, error) { - log.Log.Info("vfIsReady()", "device", pciAddr) - var err error - var vfLink netlink.Link - err = wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { - vfName := tryGetInterfaceName(pciAddr) - vfLink, err = netlink.LinkByName(vfName) - if err != nil { - log.Log.Error(err, "vfIsReady(): unable to get VF link", "device", pciAddr) - } - return err == nil, nil - }) - if err != nil { - return vfLink, err - } - return vfLink, nil -} - -func setVfAdminMac(vfAddr string, pfLink, vfLink netlink.Link) error { - log.Log.Info("setVfAdminMac()", "vf", vfAddr) - - vfID, err := dputils.GetVFID(vfAddr) - if err != nil { - log.Log.Error(err, "setVfAdminMac(): unable to get VF id", "address", vfAddr) - return err - } - - if err := netlink.LinkSetVfHardwareAddr(pfLink, vfID, vfLink.Attrs().HardwareAddr); err != nil { - return err - } - - return nil -} - -func unbindDriverIfNeeded(vfAddr string, isRdma bool) error { - if isRdma { - log.Log.Info("unbindDriverIfNeeded(): unbind driver", "device", vfAddr) - if err := Unbind(vfAddr); err != nil { - return err - } - } - return nil -} - -func getLinkType(ifaceStatus sriovnetworkv1.InterfaceExt) string { - log.Log.V(2).Info("getLinkType()", "device", ifaceStatus.PciAddress) - if ifaceStatus.Name != "" { - link, err := netlink.LinkByName(ifaceStatus.Name) - if err != nil { - log.Log.Error(err, "getLinkType(): failed to get link", "device", ifaceStatus.Name) - return "" - } - linkType := link.Attrs().EncapType - if linkType == "ether" { - return constants.LinkTypeETH - } else if linkType == "infiniband" { - return constants.LinkTypeIB - } - } - - return "" -} +// RunCommand runs a command +func (u *utilsHelper) RunCommand(command string, args ...string) (string, string, error) { + log.Log.Info("RunCommand()", "command", command, "args", args) + var stdout, stderr bytes.Buffer -func setVfGUID(vfAddr string, pfLink netlink.Link) error { - log.Log.Info("setVfGuid()", "vf", vfAddr) - vfID, err := dputils.GetVFID(vfAddr) - if err != nil { - log.Log.Error(err, "setVfGuid(): unable to get VF id", "address", vfAddr) - return err - } - guid := generateRandomGUID() - if err := netlink.LinkSetVfNodeGUID(pfLink, vfID, guid); err != nil { - return err - } - if err := netlink.LinkSetVfPortGUID(pfLink, vfID, guid); err != nil { - return err - } - if err = Unbind(vfAddr); err != nil { - return err - } + cmd := exec.Command(command, args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr - return nil + err := cmd.Run() + log.Log.V(2).Info("RunCommand()", "output", stdout.String(), "error", err) + return stdout.String(), stderr.String(), err } -func generateRandomGUID() net.HardwareAddr { +func GenerateRandomGUID() net.HardwareAddr { guid := make(net.HardwareAddr, 8) // First field is 0x01 - xfe to avoid all zero and all F invalid guids @@ -853,82 +82,6 @@ func generateRandomGUID() net.HardwareAddr { return guid } -func GetNicSriovMode(pciAddress string) (string, error) { - log.Log.V(2).Info("GetNicSriovMode()", "device", pciAddress) - - devLink, err := netlink.DevLinkGetDeviceByName("pci", pciAddress) - if err != nil { - if errors.Is(err, syscall.ENODEV) { - // the device doesn't support devlink - return "", nil - } - return "", err - } - - return devLink.Attrs.Eswitch.Mode, nil -} - -func GetPhysSwitchID(name string) (string, error) { - swIDFile := filepath.Join(sysClassNet, name, "phys_switch_id") - physSwitchID, err := os.ReadFile(swIDFile) - if err != nil { - return "", err - } - if physSwitchID != nil { - return strings.TrimSpace(string(physSwitchID)), nil - } - return "", nil -} - -func GetPhysPortName(name string) (string, error) { - devicePortNameFile := filepath.Join(sysClassNet, name, "phys_port_name") - physPortName, err := os.ReadFile(devicePortNameFile) - if err != nil { - return "", err - } - if physPortName != nil { - return strings.TrimSpace(string(physPortName)), nil - } - return "", nil -} - -func isSwitchdev(name string) bool { - switchID, err := GetPhysSwitchID(name) - if err != nil || switchID == "" { - return false - } - - return true -} - -// IsKernelLockdownMode returns true when kernel lockdown mode is enabled -func IsKernelLockdownMode(chroot bool) bool { - path := "/sys/kernel/security/lockdown" - if !chroot { - path = "/host" + path - } - out, err := RunCommand("cat", path) - log.Log.V(2).Info("IsKernelLockdownMode()", "output", out, "error", err) - if err != nil { - return false - } - return strings.Contains(out, "[integrity]") || strings.Contains(out, "[confidentiality]") -} - -// RunCommand runs a command -func RunCommand(command string, args ...string) (string, error) { - log.Log.Info("RunCommand()", "command", command, "args", args) - var stdout, stderr bytes.Buffer - - cmd := exec.Command(command, args...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - err := cmd.Run() - log.Log.V(2).Info("RunCommand()", "output", stdout.String(), "error", err) - return stdout.String(), err -} - func HashConfigMap(cm *corev1.ConfigMap) string { var keys []string for k := range cm.Data { @@ -945,97 +98,25 @@ func HashConfigMap(cm *corev1.ConfigMap) string { return hex.EncodeToString(hashed) } -func hasMellanoxInterfacesInSpec(ifaceStatuses sriovnetworkv1.InterfaceExts, ifaceSpecs sriovnetworkv1.Interfaces) bool { - for _, ifaceStatus := range ifaceStatuses { - if ifaceStatus.Vendor == VendorMellanox { - for _, iface := range ifaceSpecs { - if iface.PciAddress == ifaceStatus.PciAddress { - log.Log.V(2).Info("hasMellanoxInterfacesInSpec(): Mellanox device specified in SriovNetworkNodeState spec", - "name", ifaceStatus.Name, - "address", ifaceStatus.PciAddress) - return true - } - } +func IsCommandNotFound(err error) bool { + if exitErr, ok := err.(*exec.ExitError); ok { + if status, ok := exitErr.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 127 { + return true } } return false } -// Workaround function to handle a case where the vf default driver is stuck and not able to create the vf kernel interface. -// This function unbind the VF from the default driver and try to bind it again -// bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2045087 -func RebindVfToDefaultDriver(vfAddr string) error { - log.Log.Info("RebindVfToDefaultDriver()", "vf", vfAddr) - if err := Unbind(vfAddr); err != nil { - return err - } - if err := BindDefaultDriver(vfAddr); err != nil { - log.Log.Error(err, "RebindVfToDefaultDriver(): fail to bind default driver", "device", vfAddr) - return err - } - - log.Log.Info("RebindVfToDefaultDriver(): workaround implemented", "vf", vfAddr) - return nil -} - -func PrepareNMUdevRule(supportedVfIds []string) error { - log.Log.V(2).Info("PrepareNMUdevRule()") - dirPath := path.Join(FilesystemRoot, "/host/etc/udev/rules.d") - filePath := path.Join(dirPath, "10-nm-unmanaged.rules") - - // remove the old unmanaged rules file - if _, err := os.Stat(filePath); err == nil { - err = os.Remove(filePath) - if err != nil { - log.Log.Error(err, "failed to remove the network manager global unmanaged rule", - "path", filePath) - } - } - - // create the pf finder script for udev rules - var stdout, stderr bytes.Buffer - cmd := exec.Command("/bin/bash", path.Join(FilesystemRoot, udevDisableNM)) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - log.Log.Error(err, "PrepareNMUdevRule(): failed to prepare nmUdevRule", "stderr", stderr.String()) - return err - } - log.Log.V(2).Info("PrepareNMUdevRule()", "stdout", stdout.String()) - - //save the device list to use for udev rules - SupportedVfIds = supportedVfIds - return nil -} - -func AddUdevRule(pfPciAddress string) error { - log.Log.V(2).Info("AddUdevRule()", "device", pfPciAddress) - pathFile := udevRulesFolder - udevRuleContent := fmt.Sprintf(nmUdevRule, strings.Join(SupportedVfIds, "|"), pfPciAddress) - - err := os.MkdirAll(pathFile, os.ModePerm) - if err != nil && !os.IsExist(err) { - log.Log.Error(err, "AddUdevRule(): failed to create dir", "path", pathFile) - return err - } - - filePath := path.Join(pathFile, fmt.Sprintf("10-nm-disable-%s.rules", pfPciAddress)) - // if the file does not exist or if oldContent != newContent - // write to file and create it if it doesn't exist - err = os.WriteFile(filePath, []byte(udevRuleContent), 0666) - if err != nil { - log.Log.Error(err, "AddUdevRule(): fail to write file", "path", filePath) - return err +func GetHostExtension() string { + if vars.InChroot { + return vars.FilesystemRoot } - return nil + return filepath.Join(vars.FilesystemRoot, consts.Host) } -func RemoveUdevRule(pfPciAddress string) error { - pathFile := udevRulesFolder - filePath := path.Join(pathFile, fmt.Sprintf("10-nm-disable-%s.rules", pfPciAddress)) - err := os.Remove(filePath) - if err != nil && !os.IsNotExist(err) { - return err +func GetChrootExtension() string { + if vars.InChroot { + return vars.FilesystemRoot } - return nil + return fmt.Sprintf("chroot %s%s", vars.FilesystemRoot, consts.Host) } diff --git a/pkg/utils/utils_mlx.go b/pkg/utils/utils_mlx.go deleted file mode 100644 index ace265c79..000000000 --- a/pkg/utils/utils_mlx.go +++ /dev/null @@ -1,118 +0,0 @@ -package utils - -import ( - "fmt" - "regexp" - "strings" - - "sigs.k8s.io/controller-runtime/pkg/log" -) - -// BlueField mode representation -type BlueFieldMode int - -const ( - bluefieldDpu BlueFieldMode = iota - bluefieldConnectXMode - - internalCPUPageSupplier = "INTERNAL_CPU_PAGE_SUPPLIER" - internalCPUEswitchManager = "INTERNAL_CPU_ESWITCH_MANAGER" - internalCPUIbVporto = "INTERNAL_CPU_IB_VPORT0" - internalCPUOffloadEngine = "INTERNAL_CPU_OFFLOAD_ENGINE" - internalCPUModel = "INTERNAL_CPU_MODEL" - - ecpf = "ECPF" - extHostPf = "EXT_HOST_PF" - embeddedCPU = "EMBEDDED_CPU" - - disabled = "DISABLED" - enabled = "ENABLED" -) - -func MstConfigReadData(pciAddress string) (string, error) { - log.Log.Info("MstConfigReadData()", "device", pciAddress) - args := []string{"-e", "-d", pciAddress, "q"} - out, err := RunCommand("mstconfig", args...) - return out, err -} - -func ParseMstconfigOutput(mstOutput string, attributes []string) (fwCurrent, fwNext map[string]string) { - log.Log.Info("ParseMstconfigOutput()", "attributes", attributes) - fwCurrent = map[string]string{} - fwNext = map[string]string{} - formatRegex := regexp.MustCompile(`(?P\w+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)`) - mstOutputLines := strings.Split(mstOutput, "\n") - for _, attr := range attributes { - for _, line := range mstOutputLines { - if strings.Contains(line, attr) { - regexResult := formatRegex.FindStringSubmatch(line) - fwCurrent[attr] = regexResult[3] - fwNext[attr] = regexResult[4] - break - } - } - } - return -} - -func mellanoxBlueFieldMode(PciAddress string) (BlueFieldMode, error) { - log.Log.V(2).Info("MellanoxBlueFieldMode(): checking mode for device", "device", PciAddress) - out, err := MstConfigReadData(PciAddress) - if err != nil { - log.Log.Error(err, "MellanoxBlueFieldMode(): failed to get mlx nic fw data") - return -1, fmt.Errorf("failed to get mlx nic fw data %w", err) - } - - attrs := []string{internalCPUPageSupplier, - internalCPUEswitchManager, - internalCPUIbVporto, - internalCPUOffloadEngine, - internalCPUModel} - mstCurrentData, _ := ParseMstconfigOutput(out, attrs) - - internalCPUPageSupplierstatus, exist := mstCurrentData[internalCPUPageSupplier] - if !exist { - return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUPageSupplier) - } - - internalCPUEswitchManagerStatus, exist := mstCurrentData[internalCPUEswitchManager] - if !exist { - return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUEswitchManager) - } - - internalCPUIbVportoStatus, exist := mstCurrentData[internalCPUIbVporto] - if !exist { - return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUIbVporto) - } - - internalCPUOffloadEngineStatus, exist := mstCurrentData[internalCPUOffloadEngine] - if !exist { - return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUOffloadEngine) - } - - internalCPUModelStatus, exist := mstCurrentData[internalCPUModel] - if !exist { - return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUModel) - } - - // check for DPU - if strings.Contains(internalCPUPageSupplierstatus, ecpf) && - strings.Contains(internalCPUEswitchManagerStatus, ecpf) && - strings.Contains(internalCPUIbVportoStatus, ecpf) && - strings.Contains(internalCPUOffloadEngineStatus, enabled) && - strings.Contains(internalCPUModelStatus, embeddedCPU) { - log.Log.V(2).Info("MellanoxBlueFieldMode(): device in DPU mode", "device", PciAddress) - return bluefieldDpu, nil - } else if strings.Contains(internalCPUPageSupplierstatus, extHostPf) && - strings.Contains(internalCPUEswitchManagerStatus, extHostPf) && - strings.Contains(internalCPUIbVportoStatus, extHostPf) && - strings.Contains(internalCPUOffloadEngineStatus, disabled) && - strings.Contains(internalCPUModelStatus, embeddedCPU) { - log.Log.V(2).Info("MellanoxBlueFieldMode(): device in ConnectX mode", "device", PciAddress) - return bluefieldConnectXMode, nil - } - - log.Log.Error(err, "MellanoxBlueFieldMode(): unknown device status", - "device", PciAddress, "mstconfig-output", out) - return -1, fmt.Errorf("MellanoxBlueFieldMode(): unknown device status for %s", PciAddress) -} diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go new file mode 100644 index 000000000..ebc49fa25 --- /dev/null +++ b/pkg/vars/vars.go @@ -0,0 +1,87 @@ +package vars + +import ( + "os" + "regexp" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" +) + +var ( + // ClusterType used by the operator to specify the platform it's running on + // supported values [kubernetes,openshift] + ClusterType string + + // DevMode controls the developer mode in the operator + // developer mode allows the operator to use un-supported network devices + DevMode bool + + // EnableAdmissionController allows the user to disable the operator webhooks + EnableAdmissionController bool + + // NodeName initialize and used by the config-daemon to identify the node it's running on + NodeName = "" + + // Destdir destination directory for the checkPoint file on the host + Destdir string + + // PlatformType specify the current platform the operator is running on + PlatformType = consts.Baremetal + // PlatformsMap contains supported platforms for virtual VF + PlatformsMap = map[string]consts.PlatformTypes{ + "openstack": consts.VirtualOpenStack, + } + + // SupportedVfIds list of supported virtual functions IDs + // loaded on daemon initialization by reading the supported-nics configmap + SupportedVfIds []string + + // DpdkDrivers supported DPDK drivers for virtual functions + DpdkDrivers = []string{"igb_uio", "vfio-pci", "uio_pci_generic"} + + // InChroot global variable to mark that the config-daemon code is inside chroot on the host file system + InChroot = false + + // UsingSystemdMode global variable to mark the config-daemon is running on systemd mode + UsingSystemdMode = false + + // FilesystemRoot used by test to mock interactions with filesystem + FilesystemRoot = "" + + //Cluster variables + Config *rest.Config = nil + Scheme *runtime.Scheme = nil + + // PfPhysPortNameRe regex to find switchdev devices on the host + PfPhysPortNameRe = regexp.MustCompile(`p\d+`) + + // Namespace contains k8s namespace + Namespace = "" +) + +func init() { + ClusterType = os.Getenv("CLUSTER_TYPE") + + DevMode = false + mode := os.Getenv("DEV_MODE") + if mode == "TRUE" { + DevMode = true + } + + Destdir = "/host/tmp" + destdir := os.Getenv("DEST_DIR") + if destdir != "" { + Destdir = destdir + } + + EnableAdmissionController = false + enableAdmissionController := os.Getenv("ADMISSION_CONTROLLERS_ENABLED") + if enableAdmissionController == "True" { + EnableAdmissionController = true + } + + Namespace = os.Getenv("NAMESPACE") +} diff --git a/pkg/vendors/mellanox/mellanox.go b/pkg/vendors/mellanox/mellanox.go new file mode 100644 index 000000000..c6631ed28 --- /dev/null +++ b/pkg/vendors/mellanox/mellanox.go @@ -0,0 +1,419 @@ +package mlxutils + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "sigs.k8s.io/controller-runtime/pkg/log" + + sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" +) + +// BlueField mode representation +type BlueFieldMode int + +const ( + BluefieldDpu BlueFieldMode = iota + BluefieldConnectXMode + + internalCPUPageSupplier = "INTERNAL_CPU_PAGE_SUPPLIER" + internalCPUEswitchManager = "INTERNAL_CPU_ESWITCH_MANAGER" + internalCPUIbVporto = "INTERNAL_CPU_IB_VPORT0" + internalCPUOffloadEngine = "INTERNAL_CPU_OFFLOAD_ENGINE" + internalCPUModel = "INTERNAL_CPU_MODEL" + + ecpf = "ECPF" + extHostPf = "EXT_HOST_PF" + embeddedCPU = "EMBEDDED_CPU" + + disabled = "DISABLED" + enabled = "ENABLED" + + VendorMellanox = "15b3" + DeviceBF2 = "a2d6" + DeviceBF3 = "a2dc" + + PreconfiguredLinkType = "Preconfigured" + UknownLinkType = "Uknown" + TotalVfs = "NUM_OF_VFS" + EnableSriov = "SRIOV_EN" + LinkTypeP1 = "LINK_TYPE_P1" + LinkTypeP2 = "LINK_TYPE_P2" + MellanoxVendorID = "15b3" +) + +type MlxNic struct { + EnableSriov bool + TotalVfs int + LinkTypeP1 string + LinkTypeP2 string +} + +//go:generate ../../../bin/mockgen -destination mock/mock_mellanox.go -source mellanox.go +type MellanoxInterface interface { + MstConfigReadData(string) (string, string, error) + GetMellanoxBlueFieldMode(string) (BlueFieldMode, error) + GetMlxNicFwData(pciAddress string) (current, next *MlxNic, err error) + + MlxConfigFW(attributesToChange map[string]MlxNic) error +} + +type mellanoxHelper struct { + utils utils.CmdInterface +} + +func New(utilsHelper utils.CmdInterface) MellanoxInterface { + return &mellanoxHelper{ + utils: utilsHelper, + } +} + +func (m *mellanoxHelper) MstConfigReadData(pciAddress string) (string, string, error) { + log.Log.Info("MstConfigReadData()", "device", pciAddress) + args := []string{"-e", "-d", pciAddress, "q"} + stdout, stderr, err := m.utils.RunCommand("mstconfig", args...) + return stdout, stderr, err +} + +func (m *mellanoxHelper) GetMellanoxBlueFieldMode(PciAddress string) (BlueFieldMode, error) { + log.Log.V(2).Info("MellanoxBlueFieldMode(): checking mode for device", "device", PciAddress) + stdout, stderr, err := m.MstConfigReadData(PciAddress) + if err != nil { + log.Log.Error(err, "MellanoxBlueFieldMode(): failed to get mlx nic fw data", "stderr", stderr) + return -1, fmt.Errorf("failed to get mlx nic fw data %w", err) + } + + attrs := []string{internalCPUPageSupplier, + internalCPUEswitchManager, + internalCPUIbVporto, + internalCPUOffloadEngine, + internalCPUModel} + mstCurrentData, _ := ParseMstconfigOutput(stdout, attrs) + + internalCPUPageSupplierstatus, exist := mstCurrentData[internalCPUPageSupplier] + if !exist { + return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUPageSupplier) + } + + internalCPUEswitchManagerStatus, exist := mstCurrentData[internalCPUEswitchManager] + if !exist { + return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUEswitchManager) + } + + internalCPUIbVportoStatus, exist := mstCurrentData[internalCPUIbVporto] + if !exist { + return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUIbVporto) + } + + internalCPUOffloadEngineStatus, exist := mstCurrentData[internalCPUOffloadEngine] + if !exist { + return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUOffloadEngine) + } + + internalCPUModelStatus, exist := mstCurrentData[internalCPUModel] + if !exist { + return 0, fmt.Errorf("failed to find %s in the mstconfig output command", internalCPUModel) + } + + // check for DPU + if strings.Contains(internalCPUPageSupplierstatus, ecpf) && + strings.Contains(internalCPUEswitchManagerStatus, ecpf) && + strings.Contains(internalCPUIbVportoStatus, ecpf) && + strings.Contains(internalCPUOffloadEngineStatus, enabled) && + strings.Contains(internalCPUModelStatus, embeddedCPU) { + log.Log.V(2).Info("MellanoxBlueFieldMode(): device in DPU mode", "device", PciAddress) + return BluefieldDpu, nil + } else if strings.Contains(internalCPUPageSupplierstatus, extHostPf) && + strings.Contains(internalCPUEswitchManagerStatus, extHostPf) && + strings.Contains(internalCPUIbVportoStatus, extHostPf) && + strings.Contains(internalCPUOffloadEngineStatus, disabled) && + strings.Contains(internalCPUModelStatus, embeddedCPU) { + log.Log.V(2).Info("MellanoxBlueFieldMode(): device in ConnectX mode", "device", PciAddress) + return BluefieldConnectXMode, nil + } + + log.Log.Error(err, "MellanoxBlueFieldMode(): unknown device status", + "device", PciAddress, "mstconfig-output", stdout) + return -1, fmt.Errorf("MellanoxBlueFieldMode(): unknown device status for %s", PciAddress) +} + +func (m *mellanoxHelper) MlxConfigFW(attributesToChange map[string]MlxNic) error { + log.Log.Info("mellanox-plugin configFW()") + for pciAddr, fwArgs := range attributesToChange { + cmdArgs := []string{"-d", pciAddr, "-y", "set"} + if fwArgs.EnableSriov { + cmdArgs = append(cmdArgs, fmt.Sprintf("%s=True", EnableSriov)) + } else if fwArgs.TotalVfs == 0 { + cmdArgs = append(cmdArgs, fmt.Sprintf("%s=False", EnableSriov)) + } + if fwArgs.TotalVfs > -1 { + cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%d", TotalVfs, fwArgs.TotalVfs)) + } + if len(fwArgs.LinkTypeP1) > 0 { + cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%s", LinkTypeP1, fwArgs.LinkTypeP1)) + } + if len(fwArgs.LinkTypeP2) > 0 { + cmdArgs = append(cmdArgs, fmt.Sprintf("%s=%s", LinkTypeP2, fwArgs.LinkTypeP2)) + } + + log.Log.V(2).Info("mellanox-plugin: configFW()", "cmd-args", cmdArgs) + if len(cmdArgs) <= 4 { + continue + } + _, strerr, err := m.utils.RunCommand("mstconfig", cmdArgs...) + if err != nil { + log.Log.Error(err, "mellanox-plugin configFW(): failed", "stderr", strerr) + return err + } + } + return nil +} + +func (m *mellanoxHelper) GetMlxNicFwData(pciAddress string) (current, next *MlxNic, err error) { + log.Log.Info("mellanox-plugin getMlnxNicFwData()", "device", pciAddress) + attrs := []string{TotalVfs, EnableSriov, LinkTypeP1, LinkTypeP2} + + out, stderr, err := m.MstConfigReadData(pciAddress) + if err != nil { + log.Log.Error(err, "mellanox-plugin getMlnxNicFwData(): failed", "stderr", stderr) + return + } + mstCurrentData, mstNextData := ParseMstconfigOutput(out, attrs) + current, err = mlnxNicFromMap(mstCurrentData) + if err != nil { + log.Log.Error(err, "mellanox-plugin mlnxNicFromMap() for current mstconfig data failed") + return + } + next, err = mlnxNicFromMap(mstNextData) + if err != nil { + log.Log.Error(err, "mellanox-plugin mlnxNicFromMap() for next mstconfig data failed") + } + return +} + +func ParseMstconfigOutput(mstOutput string, attributes []string) (fwCurrent, fwNext map[string]string) { + log.Log.Info("ParseMstconfigOutput()", "attributes", attributes) + fwCurrent = map[string]string{} + fwNext = map[string]string{} + formatRegex := regexp.MustCompile(`(?P\w+)\s+(?P\S+)\s+(?P\S+)\s+(?P\S+)`) + mstOutputLines := strings.Split(mstOutput, "\n") + for _, attr := range attributes { + for _, line := range mstOutputLines { + if strings.Contains(line, attr) { + regexResult := formatRegex.FindStringSubmatch(line) + fwCurrent[attr] = regexResult[3] + fwNext[attr] = regexResult[4] + break + } + } + } + return +} + +func HasMellanoxInterfacesInSpec(ifaceStatuses sriovnetworkv1.InterfaceExts, ifaceSpecs sriovnetworkv1.Interfaces) bool { + for _, ifaceStatus := range ifaceStatuses { + if ifaceStatus.Vendor == VendorMellanox { + for _, iface := range ifaceSpecs { + if iface.PciAddress == ifaceStatus.PciAddress { + log.Log.V(2).Info("hasMellanoxInterfacesInSpec(): Mellanox device specified in SriovNetworkNodeState spec", + "name", ifaceStatus.Name, + "address", ifaceStatus.PciAddress) + return true + } + } + } + } + return false +} + +func GetPciAddressPrefix(pciAddress string) string { + return pciAddress[:len(pciAddress)-1] +} + +func IsDualPort(pciAddress string, mellanoxNicsStatus map[string]map[string]sriovnetworkv1.InterfaceExt) bool { + log.Log.Info("mellanox-plugin IsDualPort()", "address", pciAddress) + pciAddressPrefix := GetPciAddressPrefix(pciAddress) + return len(mellanoxNicsStatus[pciAddressPrefix]) > 1 +} + +// handleTotalVfs return required total VFs or max (required VFs for dual port NIC) and needReboot if totalVfs will change +func HandleTotalVfs(fwCurrent, fwNext, attrs *MlxNic, ifaceSpec sriovnetworkv1.Interface, isDualPort bool, mellanoxNicsSpec map[string]sriovnetworkv1.Interface) ( + totalVfs int, needReboot, changeWithoutReboot bool) { + totalVfs = ifaceSpec.NumVfs + // Check if the other port is changing theGetMlnxNicFwData number of VF + if isDualPort { + otherIfaceSpec := getOtherPortSpec(ifaceSpec.PciAddress, mellanoxNicsSpec) + if otherIfaceSpec != nil { + if otherIfaceSpec.NumVfs > totalVfs { + totalVfs = otherIfaceSpec.NumVfs + } + } + } + + // if the PF is externally managed we just need to check the totalVfs requested in the policy is not higher than + // the configured amount + if ifaceSpec.ExternallyManaged { + if totalVfs > fwCurrent.TotalVfs { + log.Log.Error(nil, "The nic is externallyManaged and TotalVfs configured on the system is lower then requested VFs, failing configuration", + "current", fwCurrent.TotalVfs, "requested", totalVfs) + attrs.TotalVfs = totalVfs + needReboot = true + changeWithoutReboot = false + } + return + } + + if fwCurrent.TotalVfs != totalVfs { + log.Log.V(2).Info("Changing TotalVfs, needs reboot", "current", fwCurrent.TotalVfs, "requested", totalVfs) + attrs.TotalVfs = totalVfs + needReboot = true + } + + // Remove policy then re-apply it + if !needReboot && fwNext.TotalVfs != totalVfs { + log.Log.V(2).Info("Changing TotalVfs to same as Next Boot value, doesn't require rebooting", + "current", fwCurrent.TotalVfs, "next", fwNext.TotalVfs, "requested", totalVfs) + attrs.TotalVfs = totalVfs + changeWithoutReboot = true + } + + return +} + +// handleEnableSriov based on totalVfs it decide to disable (totalVfs=0) or enable (totalVfs changed from 0) sriov +// and need reboot if enableSriov will change +func HandleEnableSriov(totalVfs int, fwCurrent, fwNext, attrs *MlxNic) (needReboot, changeWithoutReboot bool) { + if totalVfs == 0 && fwCurrent.EnableSriov { + log.Log.V(2).Info("disabling Sriov, needs reboot") + attrs.EnableSriov = false + return true, false + } else if totalVfs > 0 && !fwCurrent.EnableSriov { + log.Log.V(2).Info("enabling Sriov, needs reboot") + attrs.EnableSriov = true + return true, false + } else if totalVfs > 0 && !fwNext.EnableSriov { + attrs.EnableSriov = true + return false, true + } + + return false, false +} + +// handleLinkType based on existing linkType and requested link +func HandleLinkType(pciPrefix string, fwData, attr *MlxNic, + mellanoxNicsSpec map[string]sriovnetworkv1.Interface, + mellanoxNicsStatus map[string]map[string]sriovnetworkv1.InterfaceExt) (bool, error) { + needReboot := false + + pciAddress := pciPrefix + "0" + if firstPortSpec, ok := mellanoxNicsSpec[pciAddress]; ok { + ifaceStatus := getIfaceStatus(pciAddress, mellanoxNicsStatus) + needChange, err := isLinkTypeRequireChange(firstPortSpec, ifaceStatus, fwData.LinkTypeP1) + if err != nil { + return false, err + } + + if needChange { + log.Log.V(2).Info("Changing LinkTypeP1, needs reboot", + "from", fwData.LinkTypeP1, "to", firstPortSpec.LinkType) + attr.LinkTypeP1 = firstPortSpec.LinkType + needReboot = true + } + } + + pciAddress = pciPrefix + "1" + if secondPortSpec, ok := mellanoxNicsSpec[pciAddress]; ok { + ifaceStatus := getIfaceStatus(pciAddress, mellanoxNicsStatus) + needChange, err := isLinkTypeRequireChange(secondPortSpec, ifaceStatus, fwData.LinkTypeP2) + if err != nil { + return false, err + } + + if needChange { + log.Log.V(2).Info("Changing LinkTypeP2, needs reboot", + "from", fwData.LinkTypeP2, "to", secondPortSpec.LinkType) + attr.LinkTypeP2 = secondPortSpec.LinkType + needReboot = true + } + } + + return needReboot, nil +} + +func mlnxNicFromMap(mstData map[string]string) (*MlxNic, error) { + log.Log.Info("mellanox-plugin mlnxNicFromMap()", "data", mstData) + fwData := &MlxNic{} + if strings.Contains(mstData[EnableSriov], "True") { + fwData.EnableSriov = true + } + i, err := strconv.Atoi(mstData[TotalVfs]) + if err != nil { + return nil, err + } + + fwData.TotalVfs = i + fwData.LinkTypeP1 = getLinkType(mstData[LinkTypeP1]) + if linkTypeP2, ok := mstData[LinkTypeP2]; ok { + fwData.LinkTypeP2 = getLinkType(linkTypeP2) + } + + return fwData, nil +} + +func getLinkType(linkType string) string { + log.Log.Info("mellanox-plugin getLinkType()", "link-type", linkType) + if strings.Contains(linkType, consts.LinkTypeETH) { + return consts.LinkTypeETH + } else if strings.Contains(linkType, consts.LinkTypeIB) { + return consts.LinkTypeIB + } else if len(linkType) > 0 { + log.Log.Error(nil, "mellanox-plugin getLinkType(): link type is not one of [ETH, IB], treating as unknown", + "link-type", linkType) + return UknownLinkType + } else { + log.Log.Info("mellanox-plugin getLinkType(): LINK_TYPE_P* attribute was not found, treating as preconfigured link type") + return PreconfiguredLinkType + } +} + +func isLinkTypeRequireChange(iface sriovnetworkv1.Interface, ifaceStatus sriovnetworkv1.InterfaceExt, fwLinkType string) (bool, error) { + log.Log.Info("mellanox-plugin isLinkTypeRequireChange()", "device", iface.PciAddress) + if iface.LinkType != "" && !strings.EqualFold(ifaceStatus.LinkType, iface.LinkType) { + if !strings.EqualFold(iface.LinkType, consts.LinkTypeETH) && !strings.EqualFold(iface.LinkType, consts.LinkTypeIB) { + return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Not supported link type: %s,"+ + " supported link types: [eth, ETH, ib, and IB]", iface.LinkType) + } + if fwLinkType == UknownLinkType { + return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Unknown link type: %s", fwLinkType) + } + if fwLinkType == PreconfiguredLinkType { + return false, fmt.Errorf("mellanox-plugin OnNodeStateChange(): Network card %s does not support link type change", iface.PciAddress) + } + + return true, nil + } + + return false, nil +} + +func getOtherPortSpec(pciAddress string, mellanoxNicsSpec map[string]sriovnetworkv1.Interface) *sriovnetworkv1.Interface { + log.Log.Info("mellanox-plugin getOtherPortSpec()", "pciAddress", pciAddress) + pciAddrPrefix := GetPciAddressPrefix(pciAddress) + pciAddrSuffix := pciAddress[len(pciAddrPrefix):] + + if pciAddrSuffix == "0" { + iface := mellanoxNicsSpec[pciAddrPrefix+"1"] + return &iface + } + + iface := mellanoxNicsSpec[pciAddrPrefix+"0"] + return &iface +} + +func getIfaceStatus(pciAddress string, mellanoxNicsStatus map[string]map[string]sriovnetworkv1.InterfaceExt) sriovnetworkv1.InterfaceExt { + return mellanoxNicsStatus[GetPciAddressPrefix(pciAddress)][pciAddress] +} diff --git a/pkg/vendors/mellanox/mock/mock_mellanox.go b/pkg/vendors/mellanox/mock/mock_mellanox.go new file mode 100644 index 000000000..78c1d4903 --- /dev/null +++ b/pkg/vendors/mellanox/mock/mock_mellanox.go @@ -0,0 +1,96 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: mellanox.go + +// Package mock_mlxutils is a generated GoMock package. +package mock_mlxutils + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + mlxutils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vendors/mellanox" +) + +// MockMellanoxInterface is a mock of MellanoxInterface interface. +type MockMellanoxInterface struct { + ctrl *gomock.Controller + recorder *MockMellanoxInterfaceMockRecorder +} + +// MockMellanoxInterfaceMockRecorder is the mock recorder for MockMellanoxInterface. +type MockMellanoxInterfaceMockRecorder struct { + mock *MockMellanoxInterface +} + +// NewMockMellanoxInterface creates a new mock instance. +func NewMockMellanoxInterface(ctrl *gomock.Controller) *MockMellanoxInterface { + mock := &MockMellanoxInterface{ctrl: ctrl} + mock.recorder = &MockMellanoxInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMellanoxInterface) EXPECT() *MockMellanoxInterfaceMockRecorder { + return m.recorder +} + +// GetMellanoxBlueFieldMode mocks base method. +func (m *MockMellanoxInterface) GetMellanoxBlueFieldMode(arg0 string) (mlxutils.BlueFieldMode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMellanoxBlueFieldMode", arg0) + ret0, _ := ret[0].(mlxutils.BlueFieldMode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMellanoxBlueFieldMode indicates an expected call of GetMellanoxBlueFieldMode. +func (mr *MockMellanoxInterfaceMockRecorder) GetMellanoxBlueFieldMode(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMellanoxBlueFieldMode", reflect.TypeOf((*MockMellanoxInterface)(nil).GetMellanoxBlueFieldMode), arg0) +} + +// GetMlxNicFwData mocks base method. +func (m *MockMellanoxInterface) GetMlxNicFwData(pciAddress string) (*mlxutils.MlxNic, *mlxutils.MlxNic, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMlxNicFwData", pciAddress) + ret0, _ := ret[0].(*mlxutils.MlxNic) + ret1, _ := ret[1].(*mlxutils.MlxNic) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetMlxNicFwData indicates an expected call of GetMlxNicFwData. +func (mr *MockMellanoxInterfaceMockRecorder) GetMlxNicFwData(pciAddress interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMlxNicFwData", reflect.TypeOf((*MockMellanoxInterface)(nil).GetMlxNicFwData), pciAddress) +} + +// MlxConfigFW mocks base method. +func (m *MockMellanoxInterface) MlxConfigFW(attributesToChange map[string]mlxutils.MlxNic) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MlxConfigFW", attributesToChange) + ret0, _ := ret[0].(error) + return ret0 +} + +// MlxConfigFW indicates an expected call of MlxConfigFW. +func (mr *MockMellanoxInterfaceMockRecorder) MlxConfigFW(attributesToChange interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MlxConfigFW", reflect.TypeOf((*MockMellanoxInterface)(nil).MlxConfigFW), attributesToChange) +} + +// MstConfigReadData mocks base method. +func (m *MockMellanoxInterface) MstConfigReadData(arg0 string) (string, string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MstConfigReadData", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// MstConfigReadData indicates an expected call of MstConfigReadData. +func (mr *MockMellanoxInterfaceMockRecorder) MstConfigReadData(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MstConfigReadData", reflect.TypeOf((*MockMellanoxInterface)(nil).MstConfigReadData), arg0) +} diff --git a/pkg/webhook/validate.go b/pkg/webhook/validate.go index 366881dc6..f225dd6c2 100644 --- a/pkg/webhook/validate.go +++ b/pkg/webhook/validate.go @@ -16,8 +16,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" - constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" - "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" ) const ( @@ -35,12 +35,12 @@ func validateSriovOperatorConfig(cr *sriovnetworkv1.SriovOperatorConfig, operati log.Log.V(2).Info("validateSriovOperatorConfig", "object", cr) var warnings []string - if cr.GetName() != constants.DefaultConfigName { + if cr.GetName() != consts.DefaultConfigName { return false, warnings, fmt.Errorf("only default SriovOperatorConfig is used") } if operation == v1.Delete { - return false, warnings, fmt.Errorf("default SriovOperatorConfig shouldn't be deleted") + warnings = append(warnings, "default SriovOperatorConfig shouldn't be deleted") } if cr.Spec.DisableDrain { @@ -95,14 +95,13 @@ func validateSriovNetworkNodePolicy(cr *sriovnetworkv1.SriovNetworkNodePolicy, o log.Log.V(2).Info("validateSriovNetworkNodePolicy", "object", cr) var warnings []string - if cr.GetName() == constants.DefaultPolicyName && cr.GetNamespace() == os.Getenv("NAMESPACE") { + if cr.GetName() == consts.DefaultPolicyName && cr.GetNamespace() == os.Getenv("NAMESPACE") { if operation == v1.Delete { - // reject deletion of default policy - return false, warnings, fmt.Errorf("default SriovNetworkNodePolicy shouldn't be deleted") - } else { - // skip validating default policy - return true, warnings, nil + warnings = append(warnings, "default SriovNetworkNodePolicy shouldn't be deleted") } + + // skip validating default policy + return true, warnings, nil } if cr.GetNamespace() != os.Getenv("NAMESPACE") { @@ -110,7 +109,6 @@ func validateSriovNetworkNodePolicy(cr *sriovnetworkv1.SriovNetworkNodePolicy, o fmt.Sprintf(" is created or updated but not used. Only policy in %s namespace is respected.", os.Getenv("NAMESPACE"))) } - // DELETE should always succeed unless it's for the default object if operation == v1.Delete { return true, warnings, nil } @@ -193,19 +191,19 @@ func staticValidateSriovNetworkNodePolicy(cr *sriovnetworkv1.SriovNetworkNodePol // To configure RoCE on baremetal or virtual machine: // BM: DeviceType = netdevice && isRdma = true // VM: DeviceType = vfio-pci && isRdma = false - if cr.Spec.DeviceType == constants.DeviceTypeVfioPci && cr.Spec.IsRdma { + if cr.Spec.DeviceType == consts.DeviceTypeVfioPci && cr.Spec.IsRdma { return false, fmt.Errorf("'deviceType: vfio-pci' conflicts with 'isRdma: true'; Set 'deviceType' to (string)'netdevice' Or Set 'isRdma' to (bool)'false'") } - if strings.EqualFold(cr.Spec.LinkType, constants.LinkTypeIB) && !cr.Spec.IsRdma { + if strings.EqualFold(cr.Spec.LinkType, consts.LinkTypeIB) && !cr.Spec.IsRdma { return false, fmt.Errorf("'linkType: ib or IB' requires 'isRdma: true'; Set 'isRdma' to (bool)'true'") } // vdpa: deviceType must be set to 'netdevice' - if cr.Spec.DeviceType != constants.DeviceTypeNetDevice && (cr.Spec.VdpaType == constants.VdpaTypeVirtio || cr.Spec.VdpaType == constants.VdpaTypeVhost) { + if cr.Spec.DeviceType != consts.DeviceTypeNetDevice && (cr.Spec.VdpaType == consts.VdpaTypeVirtio || cr.Spec.VdpaType == consts.VdpaTypeVhost) { return false, fmt.Errorf("'deviceType: %s' conflicts with '%s'; Set 'deviceType' to (string)'netdevice' Or Remove 'vdpaType'", cr.Spec.DeviceType, cr.Spec.VdpaType) } // vdpa: device must be configured in switchdev mode - if (cr.Spec.VdpaType == constants.VdpaTypeVirtio || cr.Spec.VdpaType == constants.VdpaTypeVhost) && cr.Spec.EswitchMode != sriovnetworkv1.ESwithModeSwitchDev { + if (cr.Spec.VdpaType == consts.VdpaTypeVirtio || cr.Spec.VdpaType == consts.VdpaTypeVhost) && cr.Spec.EswitchMode != sriovnetworkv1.ESwithModeSwitchDev { return false, fmt.Errorf("vdpa requires the device to be configured in switchdev mode") } @@ -297,7 +295,7 @@ func validatePolicyForNodeState(policy *sriovnetworkv1.SriovNetworkNodePolicy, s if err == nil { interfaceSelected = true interfaceSelectedForNode = true - if policy.GetName() != constants.DefaultPolicyName && policy.Spec.NumVfs == 0 { + if policy.GetName() != consts.DefaultPolicyName && policy.Spec.NumVfs == 0 { return nil, fmt.Errorf("numVfs(%d) in CR %s is not allowed", policy.Spec.NumVfs, policy.GetName()) } if policy.Spec.NumVfs > iface.TotalVfs && iface.Vendor == IntelID { @@ -322,7 +320,7 @@ func validatePolicyForNodeState(policy *sriovnetworkv1.SriovNetworkNodePolicy, s } } // vdpa: only mellanox cards are supported - if (policy.Spec.VdpaType == constants.VdpaTypeVirtio || policy.Spec.VdpaType == constants.VdpaTypeVhost) && iface.Vendor != MellanoxID { + if (policy.Spec.VdpaType == consts.VdpaTypeVirtio || policy.Spec.VdpaType == consts.VdpaTypeVhost) && iface.Vendor != MellanoxID { return nil, fmt.Errorf("vendor(%s) in CR %s not supported for vdpa interface(%s)", iface.Vendor, policy.GetName(), iface.Name) } } else { @@ -440,7 +438,7 @@ func validateNicModel(selector *sriovnetworkv1.SriovNetworkNicSelector, iface *s } // Check the vendor and device ID of the VF only if we are on a virtual environment - for key := range utils.PlatformMap { + for key := range vars.PlatformsMap { if strings.Contains(strings.ToLower(node.Spec.ProviderID), strings.ToLower(key)) && selector.NetFilter != "" && selector.NetFilter == iface.NetFilter && sriovnetworkv1.IsVfSupportedModel(iface.Vendor, iface.DeviceID) { diff --git a/pkg/webhook/validate_test.go b/pkg/webhook/validate_test.go index ce50f7a35..db769015a 100644 --- a/pkg/webhook/validate_test.go +++ b/pkg/webhook/validate_test.go @@ -10,8 +10,6 @@ import ( . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/envtest" . "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" @@ -159,15 +157,16 @@ func TestValidateSriovOperatorConfigWithDefaultOperatorConfig(t *testing.T) { config := newDefaultOperatorConfig() snclient = fakesnclientset.NewSimpleClientset() - ok, _, err := validateSriovOperatorConfig(config, "DELETE") - g.Expect(err).To(HaveOccurred()) - g.Expect(ok).To(Equal(false)) + ok, w, err := validateSriovOperatorConfig(config, "DELETE") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ok).To(Equal(true)) + g.Expect(w[0]).To(ContainSubstring("default SriovOperatorConfig shouldn't be deleted")) ok, _, err = validateSriovOperatorConfig(config, "UPDATE") g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) - ok, w, err := validateSriovOperatorConfig(config, "UPDATE") + ok, w, err = validateSriovOperatorConfig(config, "UPDATE") g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) g.Expect(w[0]).To(ContainSubstring("Node draining is disabled")) @@ -227,9 +226,10 @@ func TestValidateSriovNetworkNodePolicyWithDefaultPolicy(t *testing.T) { } os.Setenv("NAMESPACE", "openshift-sriov-network-operator") g := NewGomegaWithT(t) - ok, _, err = validateSriovNetworkNodePolicy(policy, "DELETE") - g.Expect(err).To(HaveOccurred()) - g.Expect(ok).To(Equal(false)) + ok, w, err := validateSriovNetworkNodePolicy(policy, "DELETE") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ok).To(Equal(true)) + g.Expect(w[0]).To(ContainSubstring("default SriovNetworkNodePolicy shouldn't be deleted")) ok, _, err = validateSriovNetworkNodePolicy(policy, "UPDATE") g.Expect(err).NotTo(HaveOccurred()) @@ -1045,13 +1045,8 @@ func TestValidatePolicyForNodeStateWithInvalidDevice(t *testing.T) { }, } g := NewGomegaWithT(t) - var testEnv = &envtest.Environment{} - cfg, err := testEnv.Start() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(cfg).ToNot(BeNil()) - kubeclient = kubernetes.NewForConfigOrDie(cfg) - _, err = validatePolicyForNodeState(policy, state, NewNode()) + _, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).NotTo(HaveOccurred()) } diff --git a/test/conformance/tests/init.go b/test/conformance/tests/init.go index 56b836dd9..99458e8dc 100644 --- a/test/conformance/tests/init.go +++ b/test/conformance/tests/init.go @@ -18,4 +18,7 @@ func init() { } clients = testclient.New("") + if clients == nil { + panic("failed package init, failed to create ClientSet") + } } diff --git a/test/conformance/tests/test_sriov_operator.go b/test/conformance/tests/test_sriov_operator.go index 809a2c1bd..bc3ca194e 100644 --- a/test/conformance/tests/test_sriov_operator.go +++ b/test/conformance/tests/test_sriov_operator.go @@ -214,7 +214,7 @@ var _ = Describe("[sriov] operator", func() { setOperatorConfigLogLevel(2) By("Flip DisableDrain to trigger operator activity") - since := time.Now() + since := time.Now().Add(-10 * time.Second) Eventually(func() error { return cluster.SetDisableNodeDrainState(clients, operatorNamespace, !initialDisableDrain) }, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred()) @@ -240,7 +240,7 @@ var _ = Describe("[sriov] operator", func() { setOperatorConfigLogLevel(0) By("Flip DisableDrain again to trigger operator activity") - since = time.Now() + since = time.Now().Add(-10 * time.Second) Eventually(func() error { return cluster.SetDisableNodeDrainState(clients, operatorNamespace, initialDisableDrain) }, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred()) @@ -248,20 +248,32 @@ var _ = Describe("[sriov] operator", func() { By("Assert logs contains less operator activity") Eventually(func(g Gomega) { logs := getOperatorLogs(since) - g.Expect(logs).To( + + // time only contains sec, but we can have race here that in the same sec there was a sync + afterLogs := []string{} + found := false + for _, log := range logs { + if found { + afterLogs = append(afterLogs, log) + } + if strings.Contains(log, "{\"new-level\": 0, \"current-level\": 2}") { + found = true + } + } + g.Expect(found).To(BeTrue()) + g.Expect(afterLogs).To( ContainElement(And( ContainSubstring("Reconciling SriovOperatorConfig"), )), ) // Should not contain verbose logging - g.Expect(logs).ToNot( + g.Expect(afterLogs).ToNot( ContainElement( ContainSubstring("Start to sync webhook objects"), ), ) - }, 1*time.Minute, 5*time.Second).Should(Succeed()) - + }, 3*time.Minute, 5*time.Second).Should(Succeed()) }) }) }) @@ -1059,7 +1071,9 @@ var _ = Describe("[sriov] operator", func() { } vfioNode, vfioNic = sriovInfos.FindOneVfioSriovDevice() - Expect(vfioNode).ToNot(Equal("")) + if vfioNode == "" { + Skip("skip test as no vfio-pci capable PF was found") + } By("Using device " + vfioNic.Name + " on node " + vfioNode) }) @@ -1096,20 +1110,20 @@ var _ = Describe("[sriov] operator", func() { }) Context("PF Partitioning", func() { - var vfioNode string - var vfioNic sriovv1.InterfaceExt - // 27633 BeforeEach(func() { if discovery.Enabled() { Skip("Test unsuitable to be run in discovery mode") } - vfioNode, vfioNic = sriovInfos.FindOneVfioSriovDevice() - Expect(vfioNode).ToNot(Equal("")) - By("Using device " + vfioNic.Name + " on node " + vfioNode) }) It("Should be possible to partition the pf's vfs", func() { + vfioNode, vfioNic := sriovInfos.FindOneVfioSriovDevice() + if vfioNode == "" { + Skip("skip test as no vfio-pci capable PF was found") + } + By("Using device " + vfioNic.Name + " on node " + vfioNode) + _, err := network.CreateSriovPolicy(clients, "test-policy-", operatorNamespace, vfioNic.Name+"#2-4", vfioNode, 5, testResourceName, "netdevice") Expect(err).ToNot(HaveOccurred()) @@ -1193,7 +1207,7 @@ var _ = Describe("[sriov] operator", func() { capacity, _ = resNum.AsInt64() res["openshift.io/testresource1"] = capacity return res - }, 2*time.Minute, time.Second).Should(Equal(map[string]int64{ + }, 15*time.Minute, time.Second).Should(Equal(map[string]int64{ "openshift.io/testresource": int64(3), "openshift.io/testresource1": int64(2), })) @@ -1434,10 +1448,6 @@ var _ = Describe("[sriov] operator", func() { Context("MTU", func() { BeforeEach(func() { - if cluster.VirtualCluster() { - // https://bugzilla.redhat.com/show_bug.cgi?id=2214977 - Skip("Bug in IGB driver") - } var node string resourceName := "mturesource" @@ -2018,14 +2028,16 @@ var _ = Describe("[sriov] operator", func() { Context("ExternallyManaged Validation", func() { numVfs := 5 var node string - var nic sriovv1.InterfaceExt + var nic *sriovv1.InterfaceExt externallyManage := func(policy *sriovv1.SriovNetworkNodePolicy) { policy.Spec.ExternallyManaged = true } execute.BeforeAll(func() { - node, nic = sriovInfos.FindOneVfioSriovDevice() - Expect(node).ToNot(Equal("")) + var err error + node, nic, err = sriovInfos.FindOneSriovNodeAndDevice() + Expect(err).ToNot(HaveOccurred()) + By("Using device " + nic.Name + " on node " + node) }) diff --git a/test/e2e/e2e_tests_suite_test.go b/test/e2e/e2e_tests_suite_test.go index d6c132d14..920def35a 100644 --- a/test/e2e/e2e_tests_suite_test.go +++ b/test/e2e/e2e_tests_suite_test.go @@ -13,7 +13,7 @@ import ( "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -55,7 +55,7 @@ func TestSriovTests(t *testing.T) { var sriovIface *sriovnetworkv1.InterfaceExt -var _ = BeforeSuite(func(done Done) { +var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) // Go to project root directory @@ -64,7 +64,7 @@ var _ = BeforeSuite(func(done Done) { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("config", "crd", "bases"), filepath.Join("test", "util", "crds")}, - UseExistingCluster: pointer.BoolPtr(true), + UseExistingCluster: ptr.To[bool](true), } var err error @@ -102,8 +102,6 @@ var _ = BeforeSuite(func(done Done) { doneNetNsSet = make(chan error, 1) go netns.SetPfVfLinkNetNs(testPciDev, testNsPath, devPollInterval, quitNetNsSet, doneNetNsSet) } - - close(done) }) var _ = AfterSuite(func() { diff --git a/test/e2e/sriovoperatornodepolicy_test.go b/test/e2e/sriovoperatornodepolicy_test.go index 891c06d12..ba2ec5e64 100644 --- a/test/e2e/sriovoperatornodepolicy_test.go +++ b/test/e2e/sriovoperatornodepolicy_test.go @@ -28,6 +28,8 @@ var _ = Describe("Operator", func() { execute.BeforeAll(func() { clients := testclient.New("") + Expect(clients).ToNot(BeNil()) + Eventually(func() *cluster.EnabledNodes { sriovInfos, _ = cluster.DiscoverSriov(clients, testNamespace) return sriovInfos diff --git a/test/util/clean/clean.go b/test/util/clean/clean.go index 74e6b13d3..fbc1ae1c4 100644 --- a/test/util/clean/clean.go +++ b/test/util/clean/clean.go @@ -20,6 +20,10 @@ func All() error { operatorNamespace = "openshift-sriov-network-operator" } clients := client.New("") + if clients == nil { + return fmt.Errorf("failed to create ClientSet") + } + if RestoreNodeDrainState { err := cluster.SetDisableNodeDrainState(clients, operatorNamespace, false) if err != nil { diff --git a/test/util/cluster/cluster.go b/test/util/cluster/cluster.go index fdf06bdf7..b79e61ad2 100644 --- a/test/util/cluster/cluster.go +++ b/test/util/cluster/cluster.go @@ -2,6 +2,7 @@ package cluster import ( "context" + "errors" "fmt" "io" "os" @@ -17,6 +18,7 @@ import ( runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" sriovv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" testclient "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/client" "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/nodes" "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/pod" @@ -180,6 +182,24 @@ func (n *EnabledNodes) FindSriovDevicesIgnoreFilters(node string) ([]*sriovv1.In return devices, nil } +// FindOneSriovNodeAndDevice finds a cluster node with one SR-IOV devices respecting the `SRIOV_NODE_AND_DEVICE_NAME_FILTER` filter. +func (n *EnabledNodes) FindOneSriovNodeAndDevice() (string, *sriovv1.InterfaceExt, error) { + errs := []error{} + for _, node := range n.Nodes { + devices, err := n.FindSriovDevices(node) + if err != nil { + errs = append(errs, err) + continue + } + + if len(devices) > 0 { + return node, devices[0], nil + } + } + + return "", nil, fmt.Errorf("can't find any SR-IOV devices in cluster's nodes: %w", errors.Join(errs...)) +} + // FindOneVfioSriovDevice retrieves a node with a valid sriov device for vfio func (n *EnabledNodes) FindOneVfioSriovDevice() (string, sriovv1.InterfaceExt) { for _, node := range n.Nodes { @@ -323,7 +343,7 @@ func GetNodeSecureBootState(clients *testclient.ClientSet, nodeName, namespace s podDefinition.Namespace = namespace volume := corev1.Volume{Name: "host", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/"}}} - mount := corev1.VolumeMount{Name: "host", MountPath: "/host"} + mount := corev1.VolumeMount{Name: "host", MountPath: consts.Host} podDefinition = pod.RedefineWithMount(podDefinition, volume, mount) created, err := clients.Pods(namespace).Create(context.Background(), podDefinition, metav1.CreateOptions{}) if err != nil { diff --git a/test/util/helpers/helpers.go b/test/util/helpers/helpers.go new file mode 100644 index 000000000..35f1bf22f --- /dev/null +++ b/test/util/helpers/helpers.go @@ -0,0 +1,34 @@ +package helpers + +import ( + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" +) + +// GinkgoConfigureFakeFS configure fake filesystem by setting vars.FilesystemRoot +// and register Ginkgo DeferCleanup handler to clean the fs when test completed +func GinkgoConfigureFakeFS(f *fakefilesystem.FS) { + var ( + cleanFakeFs func() + err error + ) + vars.FilesystemRoot, cleanFakeFs, err = f.Use() + Expect(err).ToNot(HaveOccurred()) + DeferCleanup(cleanFakeFs) +} + +// GinkgoAssertFileContentsEquals check that content of the file +// match the expected value. +// prepends vars.FilesystemRoot to the file path to be compatible with +// GinkgoConfigureFakeFS function +func GinkgoAssertFileContentsEquals(path, expectedContent string) { + d, err := os.ReadFile(filepath.Join(vars.FilesystemRoot, path)) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, string(d)).To(Equal(expectedContent)) +} diff --git a/test/util/pod/pod.go b/test/util/pod/pod.go index 32a1549b9..4963ba3f7 100644 --- a/test/util/pod/pod.go +++ b/test/util/pod/pod.go @@ -29,7 +29,11 @@ func GetDefinition() *corev1.Pod { Spec: corev1.PodSpec{ TerminationGracePeriodSeconds: pointer.Int64Ptr(0), Containers: []corev1.Container{{Name: "test", - Image: images.Test(), + Image: images.Test(), + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"NET_RAW"}, + }}, Command: []string{"/bin/bash", "-c", "sleep INF"}}}}} return podObject diff --git a/test/util/util.go b/test/util/util.go index 0194fb5e9..096962b3c 100644 --- a/test/util/util.go +++ b/test/util/util.go @@ -26,7 +26,7 @@ import ( var ( RetryInterval = time.Second * 1 - APITimeout = time.Second * 10 + APITimeout = time.Second * 20 Timeout = time.Second * 60 CleanupRetryInterval = time.Second * 1 CleanupTimeout = time.Second * 5 diff --git a/test/validation/tests/test_validation.go b/test/validation/tests/test_validation.go index 0df4fefb9..4c7980389 100644 --- a/test/validation/tests/test_validation.go +++ b/test/validation/tests/test_validation.go @@ -24,15 +24,6 @@ var ( operatorNamespace string ) -func init() { - operatorNamespace = os.Getenv("OPERATOR_NAMESPACE") - if operatorNamespace == "" { - operatorNamespace = "openshift-sriov-network-operator" - } - - clients = testclient.New("") -} - const ( sriovOperatorDeploymentName = "sriov-network-operator" // SriovNetworkNodePolicies contains the name of the sriov network node policies CRD @@ -45,6 +36,16 @@ const ( sriovOperatorConfigs = "sriovoperatorconfigs.sriovnetwork.openshift.io" ) +var _ = BeforeSuite(func() { + operatorNamespace = os.Getenv("OPERATOR_NAMESPACE") + if operatorNamespace == "" { + operatorNamespace = "openshift-sriov-network-operator" + } + + clients = testclient.New("") + Expect(clients).ToNot(BeNil()) +}) + var _ = Describe("validation", func() { Context("sriov", func() { diff --git a/vendor/github.com/golang/glog/glog.go b/vendor/github.com/golang/glog/glog.go index 718c34f88..e108ae8b4 100644 --- a/vendor/github.com/golang/glog/glog.go +++ b/vendor/github.com/golang/glog/glog.go @@ -1,6 +1,6 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// Go support for leveled logs, analogous to https://github.com/google/glog. // -// Copyright 2013 Google Inc. All Rights Reserved. +// Copyright 2023 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -50,15 +50,15 @@ // Log files will be written to this directory instead of the // default temporary directory. // -// Other flags provide aids to debugging. +// Other flags provide aids to debugging. // // -log_backtrace_at="" -// When set to a file and line number holding a logging statement, -// such as +// A comma-separated list of file and line numbers holding a logging +// statement, such as // -log_backtrace_at=gopherflakes.go:234 -// a stack trace will be written to the Info log whenever execution -// hits that statement. (Unlike with -vmodule, the ".go" must be -// present.) +// A stack trace will be written to the Info log whenever execution +// hits one of these statements. (Unlike with -vmodule, the ".go" +// must bepresent.) // -v=0 // Enable V-leveled logging at the specified level. // -vmodule="" @@ -66,100 +66,47 @@ // where pattern is a literal file name (minus the ".go" suffix) or // "glob" pattern and N is a V level. For instance, // -vmodule=gopher*=3 -// sets the V level to 3 in all Go files whose names begin "gopher". -// +// sets the V level to 3 in all Go files whose names begin with "gopher", +// and +// -vmodule=/path/to/glog/glog_test=1 +// sets the V level to 1 in the Go file /path/to/glog/glog_test.go. +// If a glob pattern contains a slash, it is matched against the full path, +// and the file name. Otherwise, the pattern is +// matched only against the file's basename. When both -vmodule and -v +// are specified, the -vmodule values take precedence for the specified +// modules. package glog +// This file contains the parts of the log package that are shared among all +// implementations (file, envelope, and appengine). + import ( - "bufio" "bytes" "errors" - "flag" "fmt" - "io" stdLog "log" "os" - "path/filepath" + "reflect" "runtime" + "runtime/pprof" "strconv" - "strings" "sync" "sync/atomic" + "syscall" "time" -) - -// severity identifies the sort of log: info, warning etc. It also implements -// the flag.Value interface. The -stderrthreshold flag is of type severity and -// should be modified only through the flag.Value interface. The values match -// the corresponding constants in C++. -type severity int32 // sync/atomic int32 -// These constants identify the log levels in order of increasing severity. -// A message written to a high-severity log file is also written to each -// lower-severity log file. -const ( - infoLog severity = iota - warningLog - errorLog - fatalLog - numSeverity = 4 + "github.com/golang/glog/internal/logsink" + "github.com/golang/glog/internal/stackdump" ) -const severityChar = "IWEF" - -var severityName = []string{ - infoLog: "INFO", - warningLog: "WARNING", - errorLog: "ERROR", - fatalLog: "FATAL", -} - -// get returns the value of the severity. -func (s *severity) get() severity { - return severity(atomic.LoadInt32((*int32)(s))) -} - -// set sets the value of the severity. -func (s *severity) set(val severity) { - atomic.StoreInt32((*int32)(s), int32(val)) -} - -// String is part of the flag.Value interface. -func (s *severity) String() string { - return strconv.FormatInt(int64(*s), 10) -} - -// Get is part of the flag.Value interface. -func (s *severity) Get() interface{} { - return *s -} +var timeNow = time.Now // Stubbed out for testing. -// Set is part of the flag.Value interface. -func (s *severity) Set(value string) error { - var threshold severity - // Is it a known name? - if v, ok := severityByName(value); ok { - threshold = v - } else { - v, err := strconv.Atoi(value) - if err != nil { - return err - } - threshold = severity(v) - } - logging.stderrThreshold.set(threshold) - return nil -} +// MaxSize is the maximum size of a log file in bytes. +var MaxSize uint64 = 1024 * 1024 * 1800 -func severityByName(s string) (severity, bool) { - s = strings.ToUpper(s) - for i, name := range severityName { - if name == s { - return severity(i), true - } - } - return 0, false -} +// ErrNoLog is the error we return if no log file has yet been created +// for the specified log type. +var ErrNoLog = errors.New("log file not yet created") // OutputStats tracks the number of output lines and bytes written. type OutputStats struct { @@ -183,724 +130,99 @@ var Stats struct { Info, Warning, Error OutputStats } -var severityStats = [numSeverity]*OutputStats{ - infoLog: &Stats.Info, - warningLog: &Stats.Warning, - errorLog: &Stats.Error, +var severityStats = [...]*OutputStats{ + logsink.Info: &Stats.Info, + logsink.Warning: &Stats.Warning, + logsink.Error: &Stats.Error, + logsink.Fatal: nil, } -// Level is exported because it appears in the arguments to V and is -// the type of the v flag, which can be set programmatically. -// It's a distinct type because we want to discriminate it from logType. -// Variables of type level are only changed under logging.mu. -// The -v flag is read only with atomic ops, so the state of the logging -// module is consistent. - -// Level is treated as a sync/atomic int32. - -// Level specifies a level of verbosity for V logs. *Level implements -// flag.Value; the -v flag is of type Level and should be modified -// only through the flag.Value interface. +// Level specifies a level of verbosity for V logs. The -v flag is of type +// Level and should be modified only through the flag.Value interface. type Level int32 -// get returns the value of the Level. -func (l *Level) get() Level { - return Level(atomic.LoadInt32((*int32)(l))) -} - -// set sets the value of the Level. -func (l *Level) set(val Level) { - atomic.StoreInt32((*int32)(l), int32(val)) -} - -// String is part of the flag.Value interface. -func (l *Level) String() string { - return strconv.FormatInt(int64(*l), 10) -} +var metaPool sync.Pool // Pool of *logsink.Meta. -// Get is part of the flag.Value interface. -func (l *Level) Get() interface{} { - return *l -} - -// Set is part of the flag.Value interface. -func (l *Level) Set(value string) error { - v, err := strconv.Atoi(value) - if err != nil { - return err - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(Level(v), logging.vmodule.filter, false) - return nil -} - -// moduleSpec represents the setting of the -vmodule flag. -type moduleSpec struct { - filter []modulePat -} - -// modulePat contains a filter for the -vmodule flag. -// It holds a verbosity level and a file pattern to match. -type modulePat struct { - pattern string - literal bool // The pattern is a literal string - level Level -} - -// match reports whether the file matches the pattern. It uses a string -// comparison if the pattern contains no metacharacters. -func (m *modulePat) match(file string) bool { - if m.literal { - return file == m.pattern - } - match, _ := filepath.Match(m.pattern, file) - return match -} - -func (m *moduleSpec) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - var b bytes.Buffer - for i, f := range m.filter { - if i > 0 { - b.WriteRune(',') - } - fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) +// metaPoolGet returns a *logsink.Meta from metaPool as both an interface and a +// pointer, allocating a new one if necessary. (Returning the interface value +// directly avoids an allocation if there was an existing pointer in the pool.) +func metaPoolGet() (any, *logsink.Meta) { + if metai := metaPool.Get(); metai != nil { + return metai, metai.(*logsink.Meta) } - return b.String() -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported. -func (m *moduleSpec) Get() interface{} { - return nil + meta := new(logsink.Meta) + return meta, meta } -var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") - -// Syntax: -vmodule=recordio=2,file=1,gfs*=3 -func (m *moduleSpec) Set(value string) error { - var filter []modulePat - for _, pat := range strings.Split(value, ",") { - if len(pat) == 0 { - // Empty strings such as from a trailing comma can be ignored. - continue - } - patLev := strings.Split(pat, "=") - if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { - return errVmoduleSyntax - } - pattern := patLev[0] - v, err := strconv.Atoi(patLev[1]) - if err != nil { - return errors.New("syntax error: expect comma-separated list of filename=N") - } - if v < 0 { - return errors.New("negative value for vmodule level") - } - if v == 0 { - continue // Ignore. It's harmless but no point in paying the overhead. - } - // TODO: check syntax of filter? - filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(logging.verbosity, filter, true) - return nil -} - -// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters -// that require filepath.Match to be called to match the pattern. -func isLiteral(pattern string) bool { - return !strings.ContainsAny(pattern, `\*?[]`) -} - -// traceLocation represents the setting of the -log_backtrace_at flag. -type traceLocation struct { - file string - line int -} - -// isSet reports whether the trace location has been specified. -// logging.mu is held. -func (t *traceLocation) isSet() bool { - return t.line > 0 -} +type stack bool -// match reports whether the specified file and line matches the trace location. -// The argument file name is the full path, not the basename specified in the flag. -// logging.mu is held. -func (t *traceLocation) match(file string, line int) bool { - if t.line != line { - return false - } - if i := strings.LastIndex(file, "/"); i >= 0 { - file = file[i+1:] - } - return t.file == file -} - -func (t *traceLocation) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - return fmt.Sprintf("%s:%d", t.file, t.line) -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported -func (t *traceLocation) Get() interface{} { - return nil -} - -var errTraceSyntax = errors.New("syntax error: expect file.go:234") - -// Syntax: -log_backtrace_at=gopherflakes.go:234 -// Note that unlike vmodule the file extension is included here. -func (t *traceLocation) Set(value string) error { - if value == "" { - // Unset. - t.line = 0 - t.file = "" - } - fields := strings.Split(value, ":") - if len(fields) != 2 { - return errTraceSyntax - } - file, line := fields[0], fields[1] - if !strings.Contains(file, ".") { - return errTraceSyntax - } - v, err := strconv.Atoi(line) - if err != nil { - return errTraceSyntax - } - if v <= 0 { - return errors.New("negative or zero value for level") - } - logging.mu.Lock() - defer logging.mu.Unlock() - t.line = v - t.file = file - return nil -} - -// flushSyncWriter is the interface satisfied by logging destinations. -type flushSyncWriter interface { - Flush() error - Sync() error - io.Writer -} - -func init() { - flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") - flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") - flag.Var(&logging.verbosity, "v", "log level for V logs") - flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") - flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") - flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") - - // Default stderrThreshold is ERROR. - logging.stderrThreshold = errorLog - - logging.setVState(0, nil, false) - go logging.flushDaemon() -} - -// Flush flushes all pending log I/O. -func Flush() { - logging.lockAndFlushAll() -} - -// loggingT collects all the global state of the logging setup. -type loggingT struct { - // Boolean flags. Not handled atomically because the flag.Value interface - // does not let us avoid the =true, and that shorthand is necessary for - // compatibility. TODO: does this matter enough to fix? Seems unlikely. - toStderr bool // The -logtostderr flag. - alsoToStderr bool // The -alsologtostderr flag. - - // Level flag. Handled atomically. - stderrThreshold severity // The -stderrthreshold flag. - - // freeList is a list of byte buffers, maintained under freeListMu. - freeList *buffer - // freeListMu maintains the free list. It is separate from the main mutex - // so buffers can be grabbed and printed to without holding the main lock, - // for better parallelization. - freeListMu sync.Mutex - - // mu protects the remaining elements of this structure and is - // used to synchronize logging. - mu sync.Mutex - // file holds writer for each of the log types. - file [numSeverity]flushSyncWriter - // pcs is used in V to avoid an allocation when computing the caller's PC. - pcs [1]uintptr - // vmap is a cache of the V Level for each V() call site, identified by PC. - // It is wiped whenever the vmodule flag changes state. - vmap map[uintptr]Level - // filterLength stores the length of the vmodule filter chain. If greater - // than zero, it means vmodule is enabled. It may be read safely - // using sync.LoadInt32, but is only modified under mu. - filterLength int32 - // traceLocation is the state of the -log_backtrace_at flag. - traceLocation traceLocation - // These flags are modified only under lock, although verbosity may be fetched - // safely using atomic.LoadInt32. - vmodule moduleSpec // The state of the -vmodule flag. - verbosity Level // V logging level, the value of the -v flag/ -} - -// buffer holds a byte Buffer for reuse. The zero value is ready for use. -type buffer struct { - bytes.Buffer - tmp [64]byte // temporary byte array for creating headers. - next *buffer -} - -var logging loggingT - -// setVState sets a consistent state for V logging. -// l.mu is held. -func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { - // Turn verbosity off so V will not fire while we are in transition. - logging.verbosity.set(0) - // Ditto for filter length. - atomic.StoreInt32(&logging.filterLength, 0) - - // Set the new filters and wipe the pc->Level map if the filter has changed. - if setFilter { - logging.vmodule.filter = filter - logging.vmap = make(map[uintptr]Level) - } - - // Things are consistent now, so enable filtering and verbosity. - // They are enabled in order opposite to that in V. - atomic.StoreInt32(&logging.filterLength, int32(len(filter))) - logging.verbosity.set(verbosity) -} - -// getBuffer returns a new, ready-to-use buffer. -func (l *loggingT) getBuffer() *buffer { - l.freeListMu.Lock() - b := l.freeList - if b != nil { - l.freeList = b.next - } - l.freeListMu.Unlock() - if b == nil { - b = new(buffer) - } else { - b.next = nil - b.Reset() - } - return b -} - -// putBuffer returns a buffer to the free list. -func (l *loggingT) putBuffer(b *buffer) { - if b.Len() >= 256 { - // Let big buffers die a natural death. - return - } - l.freeListMu.Lock() - b.next = l.freeList - l.freeList = b - l.freeListMu.Unlock() -} - -var timeNow = time.Now // Stubbed out for testing. +const ( + noStack = stack(false) + withStack = stack(true) +) -/* -header formats a log header as defined by the C++ implementation. -It returns a buffer containing the formatted header and the user's file and line number. -The depth specifies how many stack frames above lives the source line to be identified in the log message. - -Log lines have this form: - Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... -where the fields are defined as follows: - L A single character, representing the log level (eg 'I' for INFO) - mm The month (zero padded; ie May is '05') - dd The day (zero padded) - hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds - threadid The space-padded thread ID as returned by GetTID() - file The file name - line The line number - msg The user-supplied message -*/ -func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { - _, file, line, ok := runtime.Caller(3 + depth) +func appendBacktrace(depth int, format string, args []any) (string, []any) { + // Capture a backtrace as a stackdump.Stack (both text and PC slice). + // Structured log sinks can extract the backtrace in whichever format they + // prefer (PCs or text), and Text sinks will include it as just another part + // of the log message. + // + // Use depth instead of depth+1 so that the backtrace always includes the + // log function itself - otherwise the reason for the trace appearing in the + // log may not be obvious to the reader. + dump := stackdump.Caller(depth) + + // Add an arg and an entry in the format string for the stack dump. + // + // Copy the "args" slice to avoid a rare but serious aliasing bug + // (corrupting the caller's slice if they passed it to a non-Fatal call + // using "..."). + format = format + "\n\n%v\n" + args = append(append([]any(nil), args...), dump) + + return format, args +} + +// logf writes a log message for a log function call (or log function wrapper) +// at the given depth in the current goroutine's stack. +func logf(depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) { + now := timeNow() + _, file, line, ok := runtime.Caller(depth + 1) if !ok { file = "???" line = 1 - } else { - slash := strings.LastIndex(file, "/") - if slash >= 0 { - file = file[slash+1:] - } } - return l.formatHeader(s, file, line), file, line -} -// formatHeader formats a log header using the provided file name and line number. -func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { - now := timeNow() - if line < 0 { - line = 0 // not a real line number, but acceptable to someDigits + if stack == withStack || backtraceAt(file, line) { + format, args = appendBacktrace(depth+1, format, args) } - if s > fatalLog { - s = infoLog // for safety. - } - buf := l.getBuffer() - - // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. - // It's worth about 3X. Fprintf is hard. - _, month, day := now.Date() - hour, minute, second := now.Clock() - // Lmmdd hh:mm:ss.uuuuuu threadid file:line] - buf.tmp[0] = severityChar[s] - buf.twoDigits(1, int(month)) - buf.twoDigits(3, day) - buf.tmp[5] = ' ' - buf.twoDigits(6, hour) - buf.tmp[8] = ':' - buf.twoDigits(9, minute) - buf.tmp[11] = ':' - buf.twoDigits(12, second) - buf.tmp[14] = '.' - buf.nDigits(6, 15, now.Nanosecond()/1000, '0') - buf.tmp[21] = ' ' - buf.nDigits(7, 22, pid, ' ') // TODO: should be TID - buf.tmp[29] = ' ' - buf.Write(buf.tmp[:30]) - buf.WriteString(file) - buf.tmp[0] = ':' - n := buf.someDigits(1, line) - buf.tmp[n+1] = ']' - buf.tmp[n+2] = ' ' - buf.Write(buf.tmp[:n+3]) - return buf -} - -// Some custom tiny helper functions to print the log header efficiently. - -const digits = "0123456789" -// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. -func (buf *buffer) twoDigits(i, d int) { - buf.tmp[i+1] = digits[d%10] - d /= 10 - buf.tmp[i] = digits[d%10] -} - -// nDigits formats an n-digit integer at buf.tmp[i], -// padding with pad on the left. -// It assumes d >= 0. -func (buf *buffer) nDigits(n, i, d int, pad byte) { - j := n - 1 - for ; j >= 0 && d > 0; j-- { - buf.tmp[i+j] = digits[d%10] - d /= 10 - } - for ; j >= 0; j-- { - buf.tmp[i+j] = pad - } -} - -// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. -func (buf *buffer) someDigits(i, d int) int { - // Print into the top, then copy down. We know there's space for at least - // a 10-digit number. - j := len(buf.tmp) - for { - j-- - buf.tmp[j] = digits[d%10] - d /= 10 - if d == 0 { - break - } + metai, meta := metaPoolGet() + *meta = logsink.Meta{ + Time: now, + File: file, + Line: line, + Depth: depth + 1, + Severity: severity, + Verbose: verbose, + Thread: int64(pid), } - return copy(buf.tmp[i:], buf.tmp[j:]) + sinkf(meta, format, args...) + metaPool.Put(metai) } -func (l *loggingT) println(s severity, args ...interface{}) { - buf, file, line := l.header(s, 0) - fmt.Fprintln(buf, args...) - l.output(s, buf, file, line, false) -} - -func (l *loggingT) print(s severity, args ...interface{}) { - l.printDepth(s, 1, args...) -} - -func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { - buf, file, line := l.header(s, depth) - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, false) -} - -func (l *loggingT) printf(s severity, format string, args ...interface{}) { - buf, file, line := l.header(s, 0) - fmt.Fprintf(buf, format, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, false) -} - -// printWithFileLine behaves like print but uses the provided file and line number. If -// alsoLogToStderr is true, the log message always appears on standard error; it -// will also appear in the log file unless --logtostderr is set. -func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { - buf := l.formatHeader(s, file, line) - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, alsoToStderr) -} - -// output writes the data to the log files and releases the buffer. -func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { - l.mu.Lock() - if l.traceLocation.isSet() { - if l.traceLocation.match(file, line) { - buf.Write(stacks(false)) - } - } - data := buf.Bytes() - if !flag.Parsed() { - os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) - os.Stderr.Write(data) - } else if l.toStderr { - os.Stderr.Write(data) - } else { - if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { - os.Stderr.Write(data) - } - if l.file[s] == nil { - if err := l.createFiles(s); err != nil { - os.Stderr.Write(data) // Make sure the message appears somewhere. - l.exit(err) - } - } - switch s { - case fatalLog: - l.file[fatalLog].Write(data) - fallthrough - case errorLog: - l.file[errorLog].Write(data) - fallthrough - case warningLog: - l.file[warningLog].Write(data) - fallthrough - case infoLog: - l.file[infoLog].Write(data) - } - } - if s == fatalLog { - // If we got here via Exit rather than Fatal, print no stacks. - if atomic.LoadUint32(&fatalNoStacks) > 0 { - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(1) - } - // Dump all goroutine stacks before exiting. - // First, make sure we see the trace for the current goroutine on standard error. - // If -logtostderr has been specified, the loop below will do that anyway - // as the first stack in the full dump. - if !l.toStderr { - os.Stderr.Write(stacks(false)) - } - // Write the stack trace for all goroutines to the files. - trace := stacks(true) - logExitFunc = func(error) {} // If we get a write error, we'll still exit below. - for log := fatalLog; log >= infoLog; log-- { - if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. - f.Write(trace) - } - } - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. - } - l.putBuffer(buf) - l.mu.Unlock() - if stats := severityStats[s]; stats != nil { +func sinkf(meta *logsink.Meta, format string, args ...any) { + meta.Depth++ + n, err := logsink.Printf(meta, format, args...) + if stats := severityStats[meta.Severity]; stats != nil { atomic.AddInt64(&stats.lines, 1) - atomic.AddInt64(&stats.bytes, int64(len(data))) - } -} - -// timeoutFlush calls Flush and returns when it completes or after timeout -// elapses, whichever happens first. This is needed because the hooks invoked -// by Flush may deadlock when glog.Fatal is called from a hook that holds -// a lock. -func timeoutFlush(timeout time.Duration) { - done := make(chan bool, 1) - go func() { - Flush() // calls logging.lockAndFlushAll() - done <- true - }() - select { - case <-done: - case <-time.After(timeout): - fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) - } -} - -// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. -func stacks(all bool) []byte { - // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. - n := 10000 - if all { - n = 100000 - } - var trace []byte - for i := 0; i < 5; i++ { - trace = make([]byte, n) - nbytes := runtime.Stack(trace, all) - if nbytes < len(trace) { - return trace[:nbytes] - } - n *= 2 - } - return trace -} - -// logExitFunc provides a simple mechanism to override the default behavior -// of exiting on error. Used in testing and to guarantee we reach a required exit -// for fatal logs. Instead, exit could be a function rather than a method but that -// would make its use clumsier. -var logExitFunc func(error) - -// exit is called if there is trouble creating or writing log files. -// It flushes the logs and exits the program; there's no point in hanging around. -// l.mu is held. -func (l *loggingT) exit(err error) { - fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) - // If logExitFunc is set, we do that instead of exiting. - if logExitFunc != nil { - logExitFunc(err) - return - } - l.flushAll() - os.Exit(2) -} - -// syncBuffer joins a bufio.Writer to its underlying file, providing access to the -// file's Sync method and providing a wrapper for the Write method that provides log -// file rotation. There are conflicting methods, so the file cannot be embedded. -// l.mu is held for all its methods. -type syncBuffer struct { - logger *loggingT - *bufio.Writer - file *os.File - sev severity - nbytes uint64 // The number of bytes written to this file -} - -func (sb *syncBuffer) Sync() error { - return sb.file.Sync() -} - -func (sb *syncBuffer) Write(p []byte) (n int, err error) { - if sb.nbytes+uint64(len(p)) >= MaxSize { - if err := sb.rotateFile(time.Now()); err != nil { - sb.logger.exit(err) - } + atomic.AddInt64(&stats.bytes, int64(n)) } - n, err = sb.Writer.Write(p) - sb.nbytes += uint64(n) - if err != nil { - sb.logger.exit(err) - } - return -} -// rotateFile closes the syncBuffer's file and starts a new one. -func (sb *syncBuffer) rotateFile(now time.Time) error { - if sb.file != nil { - sb.Flush() - sb.file.Close() - } - var err error - sb.file, _, err = create(severityName[sb.sev], now) - sb.nbytes = 0 if err != nil { - return err - } - - sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) - - // Write header. - var buf bytes.Buffer - fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) - fmt.Fprintf(&buf, "Running on machine: %s\n", host) - fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) - fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") - n, err := sb.file.Write(buf.Bytes()) - sb.nbytes += uint64(n) - return err -} - -// bufferSize sizes the buffer associated with each log file. It's large -// so that log records can accumulate without the logging thread blocking -// on disk I/O. The flushDaemon will block instead. -const bufferSize = 256 * 1024 - -// createFiles creates all the log files for severity from sev down to infoLog. -// l.mu is held. -func (l *loggingT) createFiles(sev severity) error { - now := time.Now() - // Files are created in decreasing severity order, so as soon as we find one - // has already been created, we can stop. - for s := sev; s >= infoLog && l.file[s] == nil; s-- { - sb := &syncBuffer{ - logger: l, - sev: s, - } - if err := sb.rotateFile(now); err != nil { - return err - } - l.file[s] = sb - } - return nil -} - -const flushInterval = 30 * time.Second - -// flushDaemon periodically flushes the log file buffers. -func (l *loggingT) flushDaemon() { - for range time.NewTicker(flushInterval).C { - l.lockAndFlushAll() - } -} - -// lockAndFlushAll is like flushAll but locks l.mu first. -func (l *loggingT) lockAndFlushAll() { - l.mu.Lock() - l.flushAll() - l.mu.Unlock() -} - -// flushAll flushes all the logs and attempts to "sync" their data to disk. -// l.mu is held. -func (l *loggingT) flushAll() { - // Flush from fatal down, in case there's trouble flushing. - for s := fatalLog; s >= infoLog; s-- { - file := l.file[s] - if file != nil { - file.Flush() // ignore error - file.Sync() // ignore error - } + logsink.Printf(meta, "glog: exiting because of error: %s", err) + sinks.file.Flush() + os.Exit(2) } } @@ -912,9 +234,9 @@ func (l *loggingT) flushAll() { // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not // recognized, CopyStandardLogTo panics. func CopyStandardLogTo(name string) { - sev, ok := severityByName(name) - if !ok { - panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) + sev, err := logsink.ParseSeverity(name) + if err != nil { + panic(fmt.Sprintf("log.CopyStandardLogTo(%q): %v", name, err)) } // Set a log format that captures the user's file and line: // d.go:23: message @@ -922,9 +244,22 @@ func CopyStandardLogTo(name string) { stdLog.SetOutput(logBridge(sev)) } +// NewStandardLogger returns a Logger that writes to the Google logs for the +// named and lower severities. +// +// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not +// recognized, NewStandardLogger panics. +func NewStandardLogger(name string) *stdLog.Logger { + sev, err := logsink.ParseSeverity(name) + if err != nil { + panic(fmt.Sprintf("log.NewStandardLogger(%q): %v", name, err)) + } + return stdLog.New(logBridge(sev), "", stdLog.Lshortfile) +} + // logBridge provides the Write method that enables CopyStandardLogTo to connect // Go's standard logs to the logs provided by this package. -type logBridge severity +type logBridge logsink.Severity // Write parses the standard logging line and passes its components to the // logger for severity(lb). @@ -946,36 +281,72 @@ func (lb logBridge) Write(b []byte) (n int, err error) { line = 1 } } - // printWithFileLine with alsoToStderr=true, so standard log messages - // always appear on standard error. - logging.printWithFileLine(severity(lb), file, line, true, text) + + // The depth below hard-codes details of how stdlog gets here. The alternative would be to walk + // up the stack looking for src/log/log.go but that seems like it would be + // unfortunately slow. + const stdLogDepth = 4 + + metai, meta := metaPoolGet() + *meta = logsink.Meta{ + Time: timeNow(), + File: file, + Line: line, + Depth: stdLogDepth, + Severity: logsink.Severity(lb), + Thread: int64(pid), + } + + format := "%s" + args := []any{text} + if backtraceAt(file, line) { + format, args = appendBacktrace(meta.Depth, format, args) + } + + sinkf(meta, format, args...) + metaPool.Put(metai) + return len(b), nil } -// setV computes and remembers the V level for a given PC -// when vmodule is enabled. -// File pattern matching takes the basename of the file, stripped -// of its .go suffix, and uses filepath.Match, which is a little more -// general than the *? matching used in C++. -// l.mu is held. -func (l *loggingT) setV(pc uintptr) Level { - fn := runtime.FuncForPC(pc) - file, _ := fn.FileLine(pc) - // The file is something like /a/b/c/d.go. We want just the d. - if strings.HasSuffix(file, ".go") { - file = file[:len(file)-3] +// defaultFormat returns a fmt.Printf format specifier that formats its +// arguments as if they were passed to fmt.Print. +func defaultFormat(args []any) string { + n := len(args) + switch n { + case 0: + return "" + case 1: + return "%v" + } + + b := make([]byte, 0, n*3-1) + wasString := true // Suppress leading space. + for _, arg := range args { + isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String + if wasString || isString { + b = append(b, "%v"...) + } else { + b = append(b, " %v"...) + } + wasString = isString } - if slash := strings.LastIndex(file, "/"); slash >= 0 { - file = file[slash+1:] + return string(b) +} + +// lnFormat returns a fmt.Printf format specifier that formats its arguments +// as if they were passed to fmt.Println. +func lnFormat(args []any) string { + if len(args) == 0 { + return "\n" } - for _, filter := range l.vmodule.filter { - if filter.match(file) { - l.vmap[pc] = filter.level - return filter.level - } + + b := make([]byte, 0, len(args)*3) + for range args { + b = append(b, "%v "...) } - l.vmap[pc] = 0 - return 0 + b[len(b)-1] = '\n' // Replace the last space with a newline. + return string(b) } // Verbose is a boolean type that implements Infof (like Printf) etc. @@ -986,9 +357,13 @@ type Verbose bool // The returned value is a boolean of type Verbose, which implements Info, Infoln // and Infof. These methods will write to the Info log if called. // Thus, one may write either +// // if glog.V(2) { glog.Info("log this") } +// // or +// // glog.V(2).Info("log this") +// // The second form is shorter but the first is cheaper if logging is off because it does // not evaluate its arguments. // @@ -997,184 +372,250 @@ type Verbose bool // V is at most the value of -v, or of -vmodule for the source file containing the // call, the V call will log. func V(level Level) Verbose { - // This function tries hard to be cheap unless there's work to do. - // The fast path is two atomic loads and compares. + return VDepth(1, level) +} - // Here is a cheap but safe test to see if V logging is enabled globally. - if logging.verbosity.get() >= level { - return Verbose(true) - } +// VDepth acts as V but uses depth to determine which call frame to check vmodule for. +// VDepth(0, level) is the same as V(level). +func VDepth(depth int, level Level) Verbose { + return Verbose(verboseEnabled(depth+1, level)) +} - // It's off globally but it vmodule may still be set. - // Here is another cheap but safe test to see if vmodule is enabled. - if atomic.LoadInt32(&logging.filterLength) > 0 { - // Now we need a proper lock to use the logging structure. The pcs field - // is shared so we must lock before accessing it. This is fairly expensive, - // but if V logging is enabled we're slow anyway. - logging.mu.Lock() - defer logging.mu.Unlock() - if runtime.Callers(2, logging.pcs[:]) == 0 { - return Verbose(false) - } - v, ok := logging.vmap[logging.pcs[0]] - if !ok { - v = logging.setV(logging.pcs[0]) - } - return Verbose(v >= level) +// Info is equivalent to the global Info function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Info(args ...any) { + v.InfoDepth(1, args...) +} + +// InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) InfoDepth(depth int, args ...any) { + if v { + logf(depth+1, logsink.Info, true, noStack, defaultFormat(args), args...) } - return Verbose(false) } -// Info is equivalent to the global Info function, guarded by the value of v. +// InfoDepthf is equivalent to the global InfoDepthf function, guarded by the value of v. // See the documentation of V for usage. -func (v Verbose) Info(args ...interface{}) { +func (v Verbose) InfoDepthf(depth int, format string, args ...any) { if v { - logging.print(infoLog, args...) + logf(depth+1, logsink.Info, true, noStack, format, args...) } } // Infoln is equivalent to the global Infoln function, guarded by the value of v. // See the documentation of V for usage. -func (v Verbose) Infoln(args ...interface{}) { +func (v Verbose) Infoln(args ...any) { if v { - logging.println(infoLog, args...) + logf(1, logsink.Info, true, noStack, lnFormat(args), args...) } } // Infof is equivalent to the global Infof function, guarded by the value of v. // See the documentation of V for usage. -func (v Verbose) Infof(format string, args ...interface{}) { +func (v Verbose) Infof(format string, args ...any) { if v { - logging.printf(infoLog, format, args...) + logf(1, logsink.Info, true, noStack, format, args...) } } // Info logs to the INFO log. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Info(args ...interface{}) { - logging.print(infoLog, args...) +func Info(args ...any) { + InfoDepth(1, args...) +} + +// InfoDepth calls Info from a different depth in the call stack. +// This enables a callee to emit logs that use the callsite information of its caller +// or any other callers in the stack. When depth == 0, the original callee's line +// information is emitted. When depth > 0, depth frames are skipped in the call stack +// and the final frame is treated like the original callee to Info. +func InfoDepth(depth int, args ...any) { + logf(depth+1, logsink.Info, false, noStack, defaultFormat(args), args...) } -// InfoDepth acts as Info but uses depth to determine which call frame to log. -// InfoDepth(0, "msg") is the same as Info("msg"). -func InfoDepth(depth int, args ...interface{}) { - logging.printDepth(infoLog, depth, args...) +// InfoDepthf acts as InfoDepth but with format string. +func InfoDepthf(depth int, format string, args ...any) { + logf(depth+1, logsink.Info, false, noStack, format, args...) } // Infoln logs to the INFO log. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Infoln(args ...interface{}) { - logging.println(infoLog, args...) +func Infoln(args ...any) { + logf(1, logsink.Info, false, noStack, lnFormat(args), args...) } // Infof logs to the INFO log. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Infof(format string, args ...interface{}) { - logging.printf(infoLog, format, args...) +func Infof(format string, args ...any) { + logf(1, logsink.Info, false, noStack, format, args...) } // Warning logs to the WARNING and INFO logs. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Warning(args ...interface{}) { - logging.print(warningLog, args...) +func Warning(args ...any) { + WarningDepth(1, args...) } // WarningDepth acts as Warning but uses depth to determine which call frame to log. // WarningDepth(0, "msg") is the same as Warning("msg"). -func WarningDepth(depth int, args ...interface{}) { - logging.printDepth(warningLog, depth, args...) +func WarningDepth(depth int, args ...any) { + logf(depth+1, logsink.Warning, false, noStack, defaultFormat(args), args...) +} + +// WarningDepthf acts as Warningf but uses depth to determine which call frame to log. +// WarningDepthf(0, "msg") is the same as Warningf("msg"). +func WarningDepthf(depth int, format string, args ...any) { + logf(depth+1, logsink.Warning, false, noStack, format, args...) } // Warningln logs to the WARNING and INFO logs. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Warningln(args ...interface{}) { - logging.println(warningLog, args...) +func Warningln(args ...any) { + logf(1, logsink.Warning, false, noStack, lnFormat(args), args...) } // Warningf logs to the WARNING and INFO logs. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Warningf(format string, args ...interface{}) { - logging.printf(warningLog, format, args...) +func Warningf(format string, args ...any) { + logf(1, logsink.Warning, false, noStack, format, args...) } // Error logs to the ERROR, WARNING, and INFO logs. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Error(args ...interface{}) { - logging.print(errorLog, args...) +func Error(args ...any) { + ErrorDepth(1, args...) } // ErrorDepth acts as Error but uses depth to determine which call frame to log. // ErrorDepth(0, "msg") is the same as Error("msg"). -func ErrorDepth(depth int, args ...interface{}) { - logging.printDepth(errorLog, depth, args...) +func ErrorDepth(depth int, args ...any) { + logf(depth+1, logsink.Error, false, noStack, defaultFormat(args), args...) +} + +// ErrorDepthf acts as Errorf but uses depth to determine which call frame to log. +// ErrorDepthf(0, "msg") is the same as Errorf("msg"). +func ErrorDepthf(depth int, format string, args ...any) { + logf(depth+1, logsink.Error, false, noStack, format, args...) } // Errorln logs to the ERROR, WARNING, and INFO logs. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Errorln(args ...interface{}) { - logging.println(errorLog, args...) +func Errorln(args ...any) { + logf(1, logsink.Error, false, noStack, lnFormat(args), args...) } // Errorf logs to the ERROR, WARNING, and INFO logs. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Errorf(format string, args ...interface{}) { - logging.printf(errorLog, format, args...) +func Errorf(format string, args ...any) { + logf(1, logsink.Error, false, noStack, format, args...) +} + +func fatalf(depth int, format string, args ...any) { + logf(depth+1, logsink.Fatal, false, withStack, format, args...) + sinks.file.Flush() + + err := abortProcess() // Should not return. + + // Failed to abort the process using signals. Dump a stack trace and exit. + Errorf("abortProcess returned unexpectedly: %v", err) + sinks.file.Flush() + pprof.Lookup("goroutine").WriteTo(os.Stderr, 1) + os.Exit(2) // Exit with the same code as the default SIGABRT handler. +} + +// abortProcess attempts to kill the current process in a way that will dump the +// currently-running goroutines someplace useful (Coroner or stderr). +// +// It does this by sending SIGABRT to the current process. Unfortunately, the +// signal may or may not be delivered to the current thread; in order to do that +// portably, we would need to add a cgo dependency and call pthread_kill. +// +// If successful, abortProcess does not return. +func abortProcess() error { + p, err := os.FindProcess(os.Getpid()) + if err != nil { + return err + } + if err := p.Signal(syscall.SIGABRT); err != nil { + return err + } + + // Sent the signal. Now we wait for it to arrive and any SIGABRT handlers to + // run (and eventually terminate the process themselves). + // + // We could just "select{}" here, but there's an outside chance that would + // trigger the runtime's deadlock detector if there happen not to be any + // background goroutines running. So we'll sleep a while first to give + // the signal some time. + time.Sleep(10 * time.Second) + select {} } // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). +// including a stack trace of all running goroutines, then calls os.Exit(2). // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Fatal(args ...interface{}) { - logging.print(fatalLog, args...) +func Fatal(args ...any) { + FatalDepth(1, args...) } // FatalDepth acts as Fatal but uses depth to determine which call frame to log. // FatalDepth(0, "msg") is the same as Fatal("msg"). -func FatalDepth(depth int, args ...interface{}) { - logging.printDepth(fatalLog, depth, args...) +func FatalDepth(depth int, args ...any) { + fatalf(depth+1, defaultFormat(args), args...) +} + +// FatalDepthf acts as Fatalf but uses depth to determine which call frame to log. +// FatalDepthf(0, "msg") is the same as Fatalf("msg"). +func FatalDepthf(depth int, format string, args ...any) { + fatalf(depth+1, format, args...) } // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). +// including a stack trace of all running goroutines, then calls os.Exit(2). // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Fatalln(args ...interface{}) { - logging.println(fatalLog, args...) +func Fatalln(args ...any) { + fatalf(1, lnFormat(args), args...) } // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). +// including a stack trace of all running goroutines, then calls os.Exit(2). // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Fatalf(format string, args ...interface{}) { - logging.printf(fatalLog, format, args...) +func Fatalf(format string, args ...any) { + fatalf(1, format, args...) } -// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. -// It allows Exit and relatives to use the Fatal logs. -var fatalNoStacks uint32 +func exitf(depth int, format string, args ...any) { + logf(depth+1, logsink.Fatal, false, noStack, format, args...) + sinks.file.Flush() + os.Exit(1) +} // Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Exit(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.print(fatalLog, args...) +func Exit(args ...any) { + ExitDepth(1, args...) } // ExitDepth acts as Exit but uses depth to determine which call frame to log. // ExitDepth(0, "msg") is the same as Exit("msg"). -func ExitDepth(depth int, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printDepth(fatalLog, depth, args...) +func ExitDepth(depth int, args ...any) { + exitf(depth+1, defaultFormat(args), args...) +} + +// ExitDepthf acts as Exitf but uses depth to determine which call frame to log. +// ExitDepthf(0, "msg") is the same as Exitf("msg"). +func ExitDepthf(depth int, format string, args ...any) { + exitf(depth+1, format, args...) } // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -func Exitln(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.println(fatalLog, args...) +func Exitln(args ...any) { + exitf(1, lnFormat(args), args...) } // Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Exitf(format string, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printf(fatalLog, format, args...) +func Exitf(format string, args ...any) { + exitf(1, format, args...) } diff --git a/vendor/github.com/golang/glog/glog_file.go b/vendor/github.com/golang/glog/glog_file.go index 65075d281..af1c934b8 100644 --- a/vendor/github.com/golang/glog/glog_file.go +++ b/vendor/github.com/golang/glog/glog_file.go @@ -1,6 +1,6 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// Go support for leveled logs, analogous to https://github.com/google/glog. // -// Copyright 2013 Google Inc. All Rights Reserved. +// Copyright 2023 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,26 +19,34 @@ package glog import ( + "bufio" + "bytes" "errors" "flag" "fmt" + "io" "os" "os/user" "path/filepath" + "runtime" "strings" "sync" "time" -) -// MaxSize is the maximum size of a log file in bytes. -var MaxSize uint64 = 1024 * 1024 * 1800 + "github.com/golang/glog/internal/logsink" +) // logDirs lists the candidate directories for new log files. var logDirs []string -// If non-empty, overrides the choice of directory in which to write logs. -// See createLogDirs for the full list of possible destinations. -var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") +var ( + // If non-empty, overrides the choice of directory in which to write logs. + // See createLogDirs for the full list of possible destinations. + logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") + logLink = flag.String("log_link", "", "If non-empty, add symbolic links in this directory to the log files") + logBufLevel = flag.Int("logbuflevel", int(logsink.Info), "Buffer log messages logged at this level or lower"+ + " (-1 means don't buffer; 0 means buffer INFO only; ...). Has limited applicability on non-prod platforms.") +) func createLogDirs() { if *logDir != "" { @@ -64,9 +72,17 @@ func init() { if err == nil { userName = current.Username } - - // Sanitize userName since it may contain filepath separators on Windows. - userName = strings.Replace(userName, `\`, "_", -1) + // Sanitize userName since it is used to construct file paths. + userName = strings.Map(func(r rune) rune { + switch { + case r >= 'a' && r <= 'z': + case r >= 'A' && r <= 'Z': + case r >= '0' && r <= '9': + default: + return '_' + } + return r + }, userName) } // shortHostname returns its argument, truncating at the first period. @@ -122,3 +138,270 @@ func create(tag string, t time.Time) (f *os.File, filename string, err error) { } return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) } + +// flushSyncWriter is the interface satisfied by logging destinations. +type flushSyncWriter interface { + Flush() error + Sync() error + io.Writer + filenames() []string +} + +var sinks struct { + stderr stderrSink + file fileSink +} + +func init() { + sinks.stderr.w = os.Stderr + + // Register stderr first: that way if we crash during file-writing at least + // the log will have gone somewhere. + logsink.TextSinks = append(logsink.TextSinks, &sinks.stderr, &sinks.file) + + sinks.file.flushChan = make(chan logsink.Severity, 1) + go sinks.file.flushDaemon() +} + +// stderrSink is a logsink.Text that writes log entries to stderr +// if they meet certain conditions. +type stderrSink struct { + mu sync.Mutex + w io.Writer +} + +// Enabled implements logsink.Text.Enabled. It returns true if any of the +// various stderr flags are enabled for logs of the given severity, if the log +// message is from the standard "log" package, or if google.Init has not yet run +// (and hence file logging is not yet initialized). +func (s *stderrSink) Enabled(m *logsink.Meta) bool { + return toStderr || alsoToStderr || m.Severity >= stderrThreshold.get() +} + +// Emit implements logsink.Text.Emit. +func (s *stderrSink) Emit(m *logsink.Meta, data []byte) (n int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + + dn, err := s.w.Write(data) + n += dn + return n, err +} + +// severityWriters is an array of flushSyncWriter with a value for each +// logsink.Severity. +type severityWriters [4]flushSyncWriter + +// fileSink is a logsink.Text that prints to a set of Google log files. +type fileSink struct { + mu sync.Mutex + // file holds writer for each of the log types. + file severityWriters + flushChan chan logsink.Severity +} + +// Enabled implements logsink.Text.Enabled. It returns true if google.Init +// has run and both --disable_log_to_disk and --logtostderr are false. +func (s *fileSink) Enabled(m *logsink.Meta) bool { + return !toStderr +} + +// Emit implements logsink.Text.Emit +func (s *fileSink) Emit(m *logsink.Meta, data []byte) (n int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + + if err = s.createMissingFiles(m.Severity); err != nil { + return 0, err + } + for sev := m.Severity; sev >= logsink.Info; sev-- { + if _, fErr := s.file[sev].Write(data); fErr != nil && err == nil { + err = fErr // Take the first error. + } + } + n = len(data) + if int(m.Severity) > *logBufLevel { + select { + case s.flushChan <- m.Severity: + default: + } + } + + return n, err +} + +// syncBuffer joins a bufio.Writer to its underlying file, providing access to the +// file's Sync method and providing a wrapper for the Write method that provides log +// file rotation. There are conflicting methods, so the file cannot be embedded. +// s.mu is held for all its methods. +type syncBuffer struct { + sink *fileSink + *bufio.Writer + file *os.File + names []string + sev logsink.Severity + nbytes uint64 // The number of bytes written to this file +} + +func (sb *syncBuffer) Sync() error { + return sb.file.Sync() +} + +func (sb *syncBuffer) Write(p []byte) (n int, err error) { + if sb.nbytes+uint64(len(p)) >= MaxSize { + if err := sb.rotateFile(time.Now()); err != nil { + return 0, err + } + } + n, err = sb.Writer.Write(p) + sb.nbytes += uint64(n) + return n, err +} + +func (sb *syncBuffer) filenames() []string { + return sb.names +} + +const footer = "\nCONTINUED IN NEXT FILE\n" + +// rotateFile closes the syncBuffer's file and starts a new one. +func (sb *syncBuffer) rotateFile(now time.Time) error { + var err error + pn := "" + file, name, err := create(sb.sev.String(), now) + + if sb.file != nil { + // The current log file becomes the previous log at the end of + // this block, so save its name for use in the header of the next + // file. + pn = sb.file.Name() + sb.Flush() + // If there's an existing file, write a footer with the name of + // the next file in the chain, followed by the constant string + // \nCONTINUED IN NEXT FILE\n to make continuation detection simple. + sb.file.Write([]byte("Next log: ")) + sb.file.Write([]byte(name)) + sb.file.Write([]byte(footer)) + sb.file.Close() + } + + sb.file = file + sb.names = append(sb.names, name) + sb.nbytes = 0 + if err != nil { + return err + } + + sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) + + // Write header. + var buf bytes.Buffer + fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) + fmt.Fprintf(&buf, "Running on machine: %s\n", host) + fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(&buf, "Previous log: %s\n", pn) + fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") + n, err := sb.file.Write(buf.Bytes()) + sb.nbytes += uint64(n) + return err +} + +// bufferSize sizes the buffer associated with each log file. It's large +// so that log records can accumulate without the logging thread blocking +// on disk I/O. The flushDaemon will block instead. +const bufferSize = 256 * 1024 + +// createMissingFiles creates all the log files for severity from infoLog up to +// upTo that have not already been created. +// s.mu is held. +func (s *fileSink) createMissingFiles(upTo logsink.Severity) error { + if s.file[upTo] != nil { + return nil + } + now := time.Now() + // Files are created in increasing severity order, so we can be assured that + // if a high severity logfile exists, then so do all of lower severity. + for sev := logsink.Info; sev <= upTo; sev++ { + if s.file[sev] != nil { + continue + } + sb := &syncBuffer{ + sink: s, + sev: sev, + } + if err := sb.rotateFile(now); err != nil { + return err + } + s.file[sev] = sb + } + return nil +} + +// flushDaemon periodically flushes the log file buffers. +func (s *fileSink) flushDaemon() { + tick := time.NewTicker(30 * time.Second) + defer tick.Stop() + for { + select { + case <-tick.C: + s.Flush() + case sev := <-s.flushChan: + s.flush(sev) + } + } +} + +// Flush flushes all pending log I/O. +func Flush() { + sinks.file.Flush() +} + +// Flush flushes all the logs and attempts to "sync" their data to disk. +func (s *fileSink) Flush() error { + return s.flush(logsink.Info) +} + +// flush flushes all logs of severity threshold or greater. +func (s *fileSink) flush(threshold logsink.Severity) error { + s.mu.Lock() + defer s.mu.Unlock() + + var firstErr error + updateErr := func(err error) { + if err != nil && firstErr == nil { + firstErr = err + } + } + + // Flush from fatal down, in case there's trouble flushing. + for sev := logsink.Fatal; sev >= threshold; sev-- { + file := s.file[sev] + if file != nil { + updateErr(file.Flush()) + updateErr(file.Sync()) + } + } + + return firstErr +} + +// Names returns the names of the log files holding the FATAL, ERROR, +// WARNING, or INFO logs. Returns ErrNoLog if the log for the given +// level doesn't exist (e.g. because no messages of that level have been +// written). This may return multiple names if the log type requested +// has rolled over. +func Names(s string) ([]string, error) { + severity, err := logsink.ParseSeverity(s) + if err != nil { + return nil, err + } + + sinks.file.mu.Lock() + defer sinks.file.mu.Unlock() + f := sinks.file.file[severity] + if f == nil { + return nil, ErrNoLog + } + + return f.filenames(), nil +} diff --git a/vendor/github.com/golang/glog/glog_flags.go b/vendor/github.com/golang/glog/glog_flags.go new file mode 100644 index 000000000..3060e54d9 --- /dev/null +++ b/vendor/github.com/golang/glog/glog_flags.go @@ -0,0 +1,395 @@ +// Go support for leveled logs, analogous to https://github.com/google/glog. +// +// Copyright 2023 Google Inc. All Rights Reserved. +// +// 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 glog + +import ( + "bytes" + "errors" + "flag" + "fmt" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + + "github.com/golang/glog/internal/logsink" +) + +// modulePat contains a filter for the -vmodule flag. +// It holds a verbosity level and a file pattern to match. +type modulePat struct { + pattern string + literal bool // The pattern is a literal string + full bool // The pattern wants to match the full path + level Level +} + +// match reports whether the file matches the pattern. It uses a string +// comparison if the pattern contains no metacharacters. +func (m *modulePat) match(full, file string) bool { + if m.literal { + if m.full { + return full == m.pattern + } + return file == m.pattern + } + if m.full { + match, _ := filepath.Match(m.pattern, full) + return match + } + match, _ := filepath.Match(m.pattern, file) + return match +} + +// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters +// that require filepath.Match to be called to match the pattern. +func isLiteral(pattern string) bool { + return !strings.ContainsAny(pattern, `\*?[]`) +} + +// isFull reports whether the pattern matches the full file path, that is, +// whether it contains /. +func isFull(pattern string) bool { + return strings.ContainsRune(pattern, '/') +} + +// verboseFlags represents the setting of the -v and -vmodule flags. +type verboseFlags struct { + // moduleLevelCache is a sync.Map storing the -vmodule Level for each V() + // call site, identified by PC. If there is no matching -vmodule filter, + // the cached value is exactly v. moduleLevelCache is replaced with a new + // Map whenever the -vmodule or -v flag changes state. + moduleLevelCache atomic.Value + + // mu guards all fields below. + mu sync.Mutex + + // v stores the value of the -v flag. It may be read safely using + // sync.LoadInt32, but is only modified under mu. + v Level + + // module stores the parsed -vmodule flag. + module []modulePat + + // moduleLength caches len(module). If greater than zero, it + // means vmodule is enabled. It may be read safely using sync.LoadInt32, but + // is only modified under mu. + moduleLength int32 +} + +// NOTE: For compatibility with the open-sourced v1 version of this +// package (github.com/golang/glog) we need to retain that flag.Level +// implements the flag.Value interface. See also go/log-vs-glog. + +// String is part of the flag.Value interface. +func (l *Level) String() string { + return strconv.FormatInt(int64(l.Get().(Level)), 10) +} + +// Get is part of the flag.Value interface. +func (l *Level) Get() any { + if l == &vflags.v { + // l is the value registered for the -v flag. + return Level(atomic.LoadInt32((*int32)(l))) + } + return *l +} + +// Set is part of the flag.Value interface. +func (l *Level) Set(value string) error { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + if l == &vflags.v { + // l is the value registered for the -v flag. + vflags.mu.Lock() + defer vflags.mu.Unlock() + vflags.moduleLevelCache.Store(&sync.Map{}) + atomic.StoreInt32((*int32)(l), int32(v)) + return nil + } + *l = Level(v) + return nil +} + +// vModuleFlag is the flag.Value for the --vmodule flag. +type vModuleFlag struct{ *verboseFlags } + +func (f vModuleFlag) String() string { + f.mu.Lock() + defer f.mu.Unlock() + + var b bytes.Buffer + for i, f := range f.module { + if i > 0 { + b.WriteRune(',') + } + fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) + } + return b.String() +} + +// Get returns nil for this flag type since the struct is not exported. +func (f vModuleFlag) Get() any { return nil } + +var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") + +// Syntax: -vmodule=recordio=2,foo/bar/baz=1,gfs*=3 +func (f vModuleFlag) Set(value string) error { + var filter []modulePat + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return errVmoduleSyntax + } + pattern := patLev[0] + v, err := strconv.Atoi(patLev[1]) + if err != nil { + return errors.New("syntax error: expect comma-separated list of filename=N") + } + // TODO: check syntax of filter? + filter = append(filter, modulePat{pattern, isLiteral(pattern), isFull(pattern), Level(v)}) + } + + f.mu.Lock() + defer f.mu.Unlock() + f.module = filter + atomic.StoreInt32((*int32)(&f.moduleLength), int32(len(f.module))) + f.moduleLevelCache.Store(&sync.Map{}) + return nil +} + +func (f *verboseFlags) levelForPC(pc uintptr) Level { + if level, ok := f.moduleLevelCache.Load().(*sync.Map).Load(pc); ok { + return level.(Level) + } + + f.mu.Lock() + defer f.mu.Unlock() + level := Level(f.v) + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + // The file is something like /a/b/c/d.go. We want just the d for + // regular matches, /a/b/c/d for full matches. + if strings.HasSuffix(file, ".go") { + file = file[:len(file)-3] + } + full := file + if slash := strings.LastIndex(file, "/"); slash >= 0 { + file = file[slash+1:] + } + for _, filter := range f.module { + if filter.match(full, file) { + level = filter.level + break // Use the first matching level. + } + } + f.moduleLevelCache.Load().(*sync.Map).Store(pc, level) + return level +} + +func (f *verboseFlags) enabled(callerDepth int, level Level) bool { + if atomic.LoadInt32(&f.moduleLength) == 0 { + // No vmodule values specified, so compare against v level. + return Level(atomic.LoadInt32((*int32)(&f.v))) >= level + } + + pcs := [1]uintptr{} + if runtime.Callers(callerDepth+2, pcs[:]) < 1 { + return false + } + frame, _ := runtime.CallersFrames(pcs[:]).Next() + return f.levelForPC(frame.Entry) >= level +} + +// traceLocation represents an entry in the -log_backtrace_at flag. +type traceLocation struct { + file string + line int +} + +var errTraceSyntax = errors.New("syntax error: expect file.go:234") + +func parseTraceLocation(value string) (traceLocation, error) { + fields := strings.Split(value, ":") + if len(fields) != 2 { + return traceLocation{}, errTraceSyntax + } + file, lineStr := fields[0], fields[1] + if !strings.Contains(file, ".") { + return traceLocation{}, errTraceSyntax + } + line, err := strconv.Atoi(lineStr) + if err != nil { + return traceLocation{}, errTraceSyntax + } + if line < 0 { + return traceLocation{}, errors.New("negative value for line") + } + return traceLocation{file, line}, nil +} + +// match reports whether the specified file and line matches the trace location. +// The argument file name is the full path, not the basename specified in the flag. +func (t traceLocation) match(file string, line int) bool { + if t.line != line { + return false + } + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + return t.file == file +} + +func (t traceLocation) String() string { + return fmt.Sprintf("%s:%d", t.file, t.line) +} + +// traceLocations represents the -log_backtrace_at flag. +// Syntax: -log_backtrace_at=recordio.go:234,sstable.go:456 +// Note that unlike vmodule the file extension is included here. +type traceLocations struct { + mu sync.Mutex + locsLen int32 // Safe for atomic read without mu. + locs []traceLocation +} + +func (t *traceLocations) String() string { + t.mu.Lock() + defer t.mu.Unlock() + + var buf bytes.Buffer + for i, tl := range t.locs { + if i > 0 { + buf.WriteString(",") + } + buf.WriteString(tl.String()) + } + return buf.String() +} + +// Get always returns nil for this flag type since the struct is not exported +func (t *traceLocations) Get() any { return nil } + +func (t *traceLocations) Set(value string) error { + var locs []traceLocation + for _, s := range strings.Split(value, ",") { + if s == "" { + continue + } + loc, err := parseTraceLocation(s) + if err != nil { + return err + } + locs = append(locs, loc) + } + + t.mu.Lock() + defer t.mu.Unlock() + atomic.StoreInt32(&t.locsLen, int32(len(locs))) + t.locs = locs + return nil +} + +func (t *traceLocations) match(file string, line int) bool { + if atomic.LoadInt32(&t.locsLen) == 0 { + return false + } + + t.mu.Lock() + defer t.mu.Unlock() + for _, tl := range t.locs { + if tl.match(file, line) { + return true + } + } + return false +} + +// severityFlag is an atomic flag.Value implementation for logsink.Severity. +type severityFlag int32 + +func (s *severityFlag) get() logsink.Severity { + return logsink.Severity(atomic.LoadInt32((*int32)(s))) +} +func (s *severityFlag) String() string { return strconv.FormatInt(int64(*s), 10) } +func (s *severityFlag) Get() any { return s.get() } +func (s *severityFlag) Set(value string) error { + threshold, err := logsink.ParseSeverity(value) + if err != nil { + // Not a severity name. Try a raw number. + v, err := strconv.Atoi(value) + if err != nil { + return err + } + threshold = logsink.Severity(v) + if threshold < logsink.Info || threshold > logsink.Fatal { + return fmt.Errorf("Severity %d out of range (min %d, max %d).", v, logsink.Info, logsink.Fatal) + } + } + atomic.StoreInt32((*int32)(s), int32(threshold)) + return nil +} + +var ( + vflags verboseFlags // The -v and -vmodule flags. + + logBacktraceAt traceLocations // The -log_backtrace_at flag. + + // Boolean flags. Not handled atomically because the flag.Value interface + // does not let us avoid the =true, and that shorthand is necessary for + // compatibility. TODO: does this matter enough to fix? Seems unlikely. + toStderr bool // The -logtostderr flag. + alsoToStderr bool // The -alsologtostderr flag. + + stderrThreshold severityFlag // The -stderrthreshold flag. +) + +// verboseEnabled returns whether the caller at the given depth should emit +// verbose logs at the given level, with depth 0 identifying the caller of +// verboseEnabled. +func verboseEnabled(callerDepth int, level Level) bool { + return vflags.enabled(callerDepth+1, level) +} + +// backtraceAt returns whether the logging call at the given function and line +// should also emit a backtrace of the current call stack. +func backtraceAt(file string, line int) bool { + return logBacktraceAt.match(file, line) +} + +func init() { + vflags.moduleLevelCache.Store(&sync.Map{}) + + flag.Var(&vflags.v, "v", "log level for V logs") + flag.Var(vModuleFlag{&vflags}, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + + flag.Var(&logBacktraceAt, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") + + stderrThreshold = severityFlag(logsink.Error) + + flag.BoolVar(&toStderr, "logtostderr", false, "log to standard error instead of files") + flag.BoolVar(&alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + flag.Var(&stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") +} diff --git a/vendor/github.com/golang/glog/internal/logsink/logsink.go b/vendor/github.com/golang/glog/internal/logsink/logsink.go new file mode 100644 index 000000000..53758e1c9 --- /dev/null +++ b/vendor/github.com/golang/glog/internal/logsink/logsink.go @@ -0,0 +1,387 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// 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 logsink + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "sync" + "time" + + "github.com/golang/glog/internal/stackdump" +) + +// MaxLogMessageLen is the limit on length of a formatted log message, including +// the standard line prefix and trailing newline. +// +// Chosen to match C++ glog. +const MaxLogMessageLen = 15000 + +// A Severity is a severity at which a message can be logged. +type Severity int8 + +// These constants identify the log levels in order of increasing severity. +// A message written to a high-severity log file is also written to each +// lower-severity log file. +const ( + Info Severity = iota + Warning + Error + + // Fatal contains logs written immediately before the process terminates. + // + // Sink implementations should not terminate the process themselves: the log + // package will perform any necessary cleanup and terminate the process as + // appropriate. + Fatal +) + +func (s Severity) String() string { + switch s { + case Info: + return "INFO" + case Warning: + return "WARNING" + case Error: + return "ERROR" + case Fatal: + return "FATAL" + } + return fmt.Sprintf("%T(%d)", s, s) +} + +// ParseSeverity returns the case-insensitive Severity value for the given string. +func ParseSeverity(name string) (Severity, error) { + name = strings.ToUpper(name) + for s := Info; s <= Fatal; s++ { + if s.String() == name { + return s, nil + } + } + return -1, fmt.Errorf("logsink: invalid severity %q", name) +} + +// Meta is metadata about a logging call. +type Meta struct { + // Time is the time at which the log call was made. + Time time.Time + + // File is the source file from which the log entry originates. + File string + // Line is the line offset within the source file. + Line int + // Depth is the number of stack frames between the logsink and the log call. + Depth int + + Severity Severity + + // Verbose indicates whether the call was made via "log.V". Log entries below + // the current verbosity threshold are not sent to the sink. + Verbose bool + + // Thread ID. This can be populated with a thread ID from another source, + // such as a system we are importing logs from. In the normal case, this + // will be set to the process ID (PID), since Go doesn't have threads. + Thread int64 + + // Stack trace starting in the logging function. May be nil. + // A logsink should implement the StackWanter interface to request this. + // + // Even if WantStack returns false, this field may be set (e.g. if another + // sink wants a stack trace). + Stack *stackdump.Stack +} + +// Structured is a logging destination that accepts structured data as input. +type Structured interface { + // Printf formats according to a fmt.Printf format specifier and writes a log + // entry. The precise result of formatting depends on the sink, but should + // aim for consistency with fmt.Printf. + // + // Printf returns the number of bytes occupied by the log entry, which + // may not be equal to the total number of bytes written. + // + // Printf returns any error encountered *if* it is severe enough that the log + // package should terminate the process. + // + // The sink must not modify the *Meta parameter, nor reference it after + // Printf has returned: it may be reused in subsequent calls. + Printf(meta *Meta, format string, a ...any) (n int, err error) +} + +// StackWanter can be implemented by a logsink.Structured to indicate that it +// wants a stack trace to accompany at least some of the log messages it receives. +type StackWanter interface { + // WantStack returns true if the sink requires a stack trace for a log message + // with this metadata. + // + // NOTE: Returning true implies that meta.Stack will be non-nil. Returning + // false does NOT imply that meta.Stack will be nil. + WantStack(meta *Meta) bool +} + +// Text is a logging destination that accepts pre-formatted log lines (instead of +// structured data). +type Text interface { + // Enabled returns whether this sink should output messages for the given + // Meta. If the sink returns false for a given Meta, the Printf function will + // not call Emit on it for the corresponding log message. + Enabled(*Meta) bool + + // Emit writes a pre-formatted text log entry (including any applicable + // header) to the log. It returns the number of bytes occupied by the entry + // (which may differ from the length of the passed-in slice). + // + // Emit returns any error encountered *if* it is severe enough that the log + // package should terminate the process. + // + // The sink must not modify the *Meta parameter, nor reference it after + // Printf has returned: it may be reused in subsequent calls. + // + // NOTE: When developing a text sink, keep in mind the surface in which the + // logs will be displayed, and whether it's important that the sink be + // resistent to tampering in the style of b/211428300. Standard text sinks + // (like `stderrSink`) do not protect against this (e.g. by escaping + // characters) because the cases where they would show user-influenced bytes + // are vanishingly small. + Emit(*Meta, []byte) (n int, err error) +} + +// bufs is a pool of *bytes.Buffer used in formatting log entries. +var bufs sync.Pool // Pool of *bytes.Buffer. + +// textPrintf formats a text log entry and emits it to all specified Text sinks. +// +// The returned n is the maximum across all Emit calls. +// The returned err is the first non-nil error encountered. +// Sinks that are disabled by configuration should return (0, nil). +func textPrintf(m *Meta, textSinks []Text, format string, args ...any) (n int, err error) { + // We expect at most file, stderr, and perhaps syslog. If there are more, + // we'll end up allocating - no big deal. + const maxExpectedTextSinks = 3 + var noAllocSinks [maxExpectedTextSinks]Text + + sinks := noAllocSinks[:0] + for _, s := range textSinks { + if s.Enabled(m) { + sinks = append(sinks, s) + } + } + if len(sinks) == 0 && m.Severity != Fatal { + return 0, nil // No TextSinks specified; don't bother formatting. + } + + bufi := bufs.Get() + var buf *bytes.Buffer + if bufi == nil { + buf = bytes.NewBuffer(nil) + bufi = buf + } else { + buf = bufi.(*bytes.Buffer) + buf.Reset() + } + + // Lmmdd hh:mm:ss.uuuuuu PID/GID file:line] + // + // The "PID" entry arguably ought to be TID for consistency with other + // environments, but TID is not meaningful in a Go program due to the + // multiplexing of goroutines across threads. + // + // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. + // It's worth about 3X. Fprintf is hard. + const severityChar = "IWEF" + buf.WriteByte(severityChar[m.Severity]) + + _, month, day := m.Time.Date() + hour, minute, second := m.Time.Clock() + twoDigits(buf, int(month)) + twoDigits(buf, day) + buf.WriteByte(' ') + twoDigits(buf, hour) + buf.WriteByte(':') + twoDigits(buf, minute) + buf.WriteByte(':') + twoDigits(buf, second) + buf.WriteByte('.') + nDigits(buf, 6, uint64(m.Time.Nanosecond()/1000), '0') + buf.WriteByte(' ') + + nDigits(buf, 7, uint64(m.Thread), ' ') + buf.WriteByte(' ') + + { + file := m.File + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + buf.WriteString(file) + } + + buf.WriteByte(':') + { + var tmp [19]byte + buf.Write(strconv.AppendInt(tmp[:0], int64(m.Line), 10)) + } + buf.WriteString("] ") + + msgStart := buf.Len() + fmt.Fprintf(buf, format, args...) + if buf.Len() > MaxLogMessageLen-1 { + buf.Truncate(MaxLogMessageLen - 1) + } + msgEnd := buf.Len() + if b := buf.Bytes(); b[len(b)-1] != '\n' { + buf.WriteByte('\n') + } + + for _, s := range sinks { + sn, sErr := s.Emit(m, buf.Bytes()) + if sn > n { + n = sn + } + if sErr != nil && err == nil { + err = sErr + } + } + + if m.Severity == Fatal { + savedM := *m + fatalMessageStore(savedEntry{ + meta: &savedM, + msg: buf.Bytes()[msgStart:msgEnd], + }) + } else { + bufs.Put(bufi) + } + return n, err +} + +const digits = "0123456789" + +// twoDigits formats a zero-prefixed two-digit integer to buf. +func twoDigits(buf *bytes.Buffer, d int) { + buf.WriteByte(digits[(d/10)%10]) + buf.WriteByte(digits[d%10]) +} + +// nDigits formats an n-digit integer to buf, padding with pad on the left. It +// assumes d != 0. +func nDigits(buf *bytes.Buffer, n int, d uint64, pad byte) { + var tmp [20]byte + + cutoff := len(tmp) - n + j := len(tmp) - 1 + for ; d > 0; j-- { + tmp[j] = digits[d%10] + d /= 10 + } + for ; j >= cutoff; j-- { + tmp[j] = pad + } + j++ + buf.Write(tmp[j:]) +} + +// Printf writes a log entry to all registered TextSinks in this package, then +// to all registered StructuredSinks. +// +// The returned n is the maximum across all Emit and Printf calls. +// The returned err is the first non-nil error encountered. +// Sinks that are disabled by configuration should return (0, nil). +func Printf(m *Meta, format string, args ...any) (n int, err error) { + m.Depth++ + n, err = textPrintf(m, TextSinks, format, args...) + + for _, sink := range StructuredSinks { + // TODO: Support TextSinks that implement StackWanter? + if sw, ok := sink.(StackWanter); ok && sw.WantStack(m) { + if m.Stack == nil { + // First, try to find a stacktrace in args, otherwise generate one. + for _, arg := range args { + if stack, ok := arg.(stackdump.Stack); ok { + m.Stack = &stack + break + } + } + if m.Stack == nil { + stack := stackdump.Caller( /* skipDepth = */ m.Depth) + m.Stack = &stack + } + } + } + sn, sErr := sink.Printf(m, format, args...) + if sn > n { + n = sn + } + if sErr != nil && err == nil { + err = sErr + } + } + return n, err +} + +// The sets of sinks to which logs should be written. +// +// These must only be modified during package init, and are read-only thereafter. +var ( + // StructuredSinks is the set of Structured sink instances to which logs + // should be written. + StructuredSinks []Structured + + // TextSinks is the set of Text sink instances to which logs should be + // written. + // + // These are registered separately from Structured sink implementations to + // avoid the need to repeat the work of formatting a message for each Text + // sink that writes it. The package-level Printf function writes to both sets + // independenty, so a given log destination should only register a Structured + // *or* a Text sink (not both). + TextSinks []Text +) + +type savedEntry struct { + meta *Meta + msg []byte +} + +// StructuredTextWrapper is a Structured sink which forwards logs to a set of Text sinks. +// +// The purpose of this sink is to allow applications to intercept logging calls before they are +// serialized and sent to Text sinks. For example, if one needs to redact PII from logging +// arguments before they reach STDERR, one solution would be to do the redacting in a Structured +// sink that forwards logs to a StructuredTextWrapper instance, and make STDERR a child of that +// StructuredTextWrapper instance. This is how one could set this up in their application: +// +// func init() { +// +// wrapper := logsink.StructuredTextWrapper{TextSinks: logsink.TextSinks} +// // sanitizersink will intercept logs and remove PII +// sanitizer := sanitizersink{Sink: &wrapper} +// logsink.StructuredSinks = append(logsink.StructuredSinks, &sanitizer) +// logsink.TextSinks = nil +// +// } +type StructuredTextWrapper struct { + // TextSinks is the set of Text sinks that should receive logs from this + // StructuredTextWrapper instance. + TextSinks []Text +} + +// Printf forwards logs to all Text sinks registered in the StructuredTextWrapper. +func (w *StructuredTextWrapper) Printf(meta *Meta, format string, args ...any) (n int, err error) { + return textPrintf(meta, w.TextSinks, format, args...) +} diff --git a/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go b/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go new file mode 100644 index 000000000..3dc269abc --- /dev/null +++ b/vendor/github.com/golang/glog/internal/logsink/logsink_fatal.go @@ -0,0 +1,35 @@ +package logsink + +import ( + "sync/atomic" + "unsafe" +) + +func fatalMessageStore(e savedEntry) { + // Only put a new one in if we haven't assigned before. + atomic.CompareAndSwapPointer(&fatalMessage, nil, unsafe.Pointer(&e)) +} + +var fatalMessage unsafe.Pointer // savedEntry stored with CompareAndSwapPointer + +// FatalMessage returns the Meta and message contents of the first message +// logged with Fatal severity, or false if none has occurred. +func FatalMessage() (*Meta, []byte, bool) { + e := (*savedEntry)(atomic.LoadPointer(&fatalMessage)) + if e == nil { + return nil, nil, false + } + return e.meta, e.msg, true +} + +// DoNotUseRacyFatalMessage is FatalMessage, but worse. +// +//go:norace +//go:nosplit +func DoNotUseRacyFatalMessage() (*Meta, []byte, bool) { + e := (*savedEntry)(fatalMessage) + if e == nil { + return nil, nil, false + } + return e.meta, e.msg, true +} diff --git a/vendor/github.com/golang/glog/internal/stackdump/stackdump.go b/vendor/github.com/golang/glog/internal/stackdump/stackdump.go new file mode 100644 index 000000000..3427c9d6b --- /dev/null +++ b/vendor/github.com/golang/glog/internal/stackdump/stackdump.go @@ -0,0 +1,127 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// 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 stackdump provides wrappers for runtime.Stack and runtime.Callers +// with uniform support for skipping caller frames. +// +// ⚠ Unlike the functions in the runtime package, these may allocate a +// non-trivial quantity of memory: use them with care. ⚠ +package stackdump + +import ( + "bytes" + "runtime" +) + +// runtimeStackSelfFrames is 1 if runtime.Stack includes the call to +// runtime.Stack itself or 0 if it does not. +// +// As of 2016-04-27, the gccgo compiler includes runtime.Stack but the gc +// compiler does not. +var runtimeStackSelfFrames = func() int { + for n := 1 << 10; n < 1<<20; n *= 2 { + buf := make([]byte, n) + n := runtime.Stack(buf, false) + if bytes.Contains(buf[:n], []byte("runtime.Stack")) { + return 1 + } else if n < len(buf) || bytes.Count(buf, []byte("\n")) >= 3 { + return 0 + } + } + return 0 +}() + +// Stack is a stack dump for a single goroutine. +type Stack struct { + // Text is a representation of the stack dump in a human-readable format. + Text []byte + + // PC is a representation of the stack dump using raw program counter values. + PC []uintptr +} + +func (s Stack) String() string { return string(s.Text) } + +// Caller returns the Stack dump for the calling goroutine, starting skipDepth +// frames before the caller of Caller. (Caller(0) provides a dump starting at +// the caller of this function.) +func Caller(skipDepth int) Stack { + return Stack{ + Text: CallerText(skipDepth + 1), + PC: CallerPC(skipDepth + 1), + } +} + +// CallerText returns a textual dump of the stack starting skipDepth frames before +// the caller. (CallerText(0) provides a dump starting at the caller of this +// function.) +func CallerText(skipDepth int) []byte { + for n := 1 << 10; ; n *= 2 { + buf := make([]byte, n) + n := runtime.Stack(buf, false) + if n < len(buf) { + return pruneFrames(skipDepth+1+runtimeStackSelfFrames, buf[:n]) + } + } +} + +// CallerPC returns a dump of the program counters of the stack starting +// skipDepth frames before the caller. (CallerPC(0) provides a dump starting at +// the caller of this function.) +func CallerPC(skipDepth int) []uintptr { + for n := 1 << 8; ; n *= 2 { + buf := make([]uintptr, n) + n := runtime.Callers(skipDepth+2, buf) + if n < len(buf) { + return buf[:n] + } + } +} + +// pruneFrames removes the topmost skipDepth frames of the first goroutine in a +// textual stack dump. It overwrites the passed-in slice. +// +// If there are fewer than skipDepth frames in the first goroutine's stack, +// pruneFrames prunes it to an empty stack and leaves the remaining contents +// intact. +func pruneFrames(skipDepth int, stack []byte) []byte { + headerLen := 0 + for i, c := range stack { + if c == '\n' { + headerLen = i + 1 + break + } + } + if headerLen == 0 { + return stack // No header line - not a well-formed stack trace. + } + + skipLen := headerLen + skipNewlines := skipDepth * 2 + for ; skipLen < len(stack) && skipNewlines > 0; skipLen++ { + c := stack[skipLen] + if c != '\n' { + continue + } + skipNewlines-- + skipLen++ + if skipNewlines == 0 || skipLen == len(stack) || stack[skipLen] == '\n' { + break + } + } + + pruned := stack[skipLen-headerLen:] + copy(pruned, stack[:headerLen]) + return pruned +} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go index 2000064a8..5627d70e3 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 f364b3418..d60ab1b41 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 c9b69937a..73687de74 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 98bf56b73..fb9458218 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 62377d2ff..c02d9ed33 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 f8da30876..7b15e15f6 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 55fa8d025..ba3e8ff8a 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 67e5b0115..d631fd664 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 3f40b9bd7..f780d5c80 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 0e6a96aa4..7de61065f 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 244c501b7..ea85780f0 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 abc89c104..e7d3df4bd 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 db9171c2e..269e173ca 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 e0fcd9b3d..a4fcef0e0 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 2b99c349a..1e63615c5 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 d702d4adc..6496c3100 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 fe36a7391..4fd1f54da 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 e5b9a8489..42f7eb9e4 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 d560019ea..f8902667e 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 8fd101d07..3b4734870 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 7ed38e43c..67e29f317 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 8ef1d5140..d6ae269ce 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 98ae02760..01e5e253c 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 565357288..2abf12f6e 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 21231d2ce..f84bae712 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 6783b26c6..f08f62807 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 19d498934..bdfc024d2 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 e42eb81d5..2e8c99612 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 c46aab339..2c394b11e 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 5e7a1169c..fab586a2c 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 f8c5394c1..f949ec547 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 3b54e1858..2f67ba86d 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 0b7c6adb8..a08657890 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 394a3965b..6fb7cb77d 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 65a998508..d78513461 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 8fc08ad0a..623a5e697 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 a388e59a0..bb6a64fe9 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 2499f977b..1ebf11782 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 a52026557..1095fd31d 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 b0f2bc4ae..b9f0e277b 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 29ccc4d13..a96da71f4 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 cedaf7e02..7753fddea 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 e9b991258..6200876fb 100644 --- a/vendor/golang.org/x/sys/unix/fcntl.go +++ b/vendor/golang.org/x/sys/unix/fcntl.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd 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 29d44808b..13b4acd5c 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 a8068f94f..9e83d18cd 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 e377cc9f4..c8bde601e 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 b06f52d74..aca5721dd 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 f98a1c542..d468b7b47 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 e60e49a3d..972d61bd7 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 15721a510..848840ae4 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_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index 0d12c0851..dbe680eab 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) } + +// IoctlLoopConfigure configures all loop device parameters in a single step +func IoctlLoopConfigure(fd int, value *LoopConfig) error { + return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value)) +} diff --git a/vendor/golang.org/x/sys/unix/ioctl_signed.go b/vendor/golang.org/x/sys/unix/ioctl_signed.go index 7def9580e..5b0759bd8 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 649913d1e..20f470b9d 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 cdc21bf76..c8b2a750f 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 47fa6a7eb..6202638ba 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -519,6 +519,7 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || + $2 == "LOOP_CONFIGURE" || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || @@ -560,7 +561,7 @@ ccflags="$@" $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || $2 ~ /^CLONE_[A-Z_]+/ || - $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && + $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ && $2 ~ /^(BPF|DLT)_/ || $2 ~ /^AUDIT_/ || $2 ~ /^(CLOCK|TIMER)_/ || @@ -663,7 +664,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 ca0513632..4b68e5978 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 fa93d0aa9..fd45fe529 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 53f1b4c5b..4d0a3430e 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 eb48294b2..6a09af53e 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 463c3eff7..3f0975f3d 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 ed0509a01..a4d35db5d 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 6f6c5fec5..714d2aae7 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 706e1322a..4a9f6634c 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 4d6257569..dbd2b6ccb 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 2a4ba47c4..130398b6b 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 3865943f6..c3a62dbb1 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 0840fe4a5..4a1eab37e 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 63e8c8383..5ea74da98 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 e94e6cdac..67ce6cef2 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 f2871fa95..1fdaa4760 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 75718ec0f..c87f9a9f4 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 4217de518..a00c3e545 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 @@ -317,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) 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 b37310ce9..0eaecf5fc 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 d51ec9963..f36c6707c 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 53c96641f..16dc69937 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 4e2d32120..14bab6b2d 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 b8da51004..3967bca77 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 47155c483..eff19ada2 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 08932093f..4f24b517a 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 d151a0d0e..ac30759ec 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 d5cd64b37..aab725ca7 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 381fd4673..ba46651f8 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 7cf54a3e4..df89f9e6b 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 87db5a6a8..a863f7052 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 fb4e50224..0f85e29e6 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) ( } //sys fchmodat(dirfd int, path string, mode uint32) (err error) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - // Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior - // and check the flags. Otherwise the mode would be applied to the symlink - // destination which is not what the user expects. - if flags&^AT_SYMLINK_NOFOLLOW != 0 { - return EINVAL - } else if flags&AT_SYMLINK_NOFOLLOW != 0 { - return EOPNOTSUPP +//sys fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // Linux fchmodat doesn't support the flags parameter, but fchmodat2 does. + // Try fchmodat2 if flags are specified. + if flags != 0 { + err := fchmodat2(dirfd, path, mode, flags) + if err == ENOSYS { + // fchmodat2 isn't available. If the flags are known to be valid, + // return EOPNOTSUPP to indicate that fchmodat doesn't support them. + if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EINVAL + } else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EOPNOTSUPP + } + } + return err } return fchmodat(dirfd, path, mode) } @@ -417,7 +425,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-- @@ -1301,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) { @@ -2482,3 +2491,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 c7d9945ea..506dafa7b 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 08086ac6a..38d55641b 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 70601ce36..d557cf8de 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 8b0f0f3aa..facdb83b2 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 da2986415..cd2dd797f 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 f5266689a..cf2ee6c75 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 2b1168d7d..ffc4c2b63 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 9843fb489..9ebfdcf44 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 a6008fccd..5f2b57c4c 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 7740af242..d1a3ad826 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 e16a12299..f2f67423e 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 f6ab02ec1..3d0e98451 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 93fe59d25..70963a95a 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 aae7f0ffd..c218ebd28 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 66eff19a3..e6c48500c 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 806aa2574..7286a9aa8 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 5e6ceee12..6f5a28894 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 2f89e8f5d..66f31210d 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 7ca064ae7..11d1f1698 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 5199d282f..7a5eb5743 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 70a9c52e9..62d8957ae 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 3eb5942f9..ce6a06885 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 fc6ccfd81..d46d689d1 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 6f34479b5..b25343c71 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) @@ -171,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) { //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL +//sys fcntl(fd int, cmd int, arg int) (n int, err error) +//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL + +// FcntlInt performs a fcntl syscall on fd with the provided command and argument. +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return fcntl(int(fd), cmd, arg) +} + +// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. +func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { + _, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk)) + return err +} + //sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { @@ -326,4 +335,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 6baabcdcb..9ddc89f4f 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 bab25360e..70a3c96ee 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 8eed3c4d4..265caa87f 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 483dde99d..ac4fda171 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 04aa43f41..0a451e6dd 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 c2796139c..30a308cbb 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 23199a7ff..ea954330f 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 b99cfa134..21974af06 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-- @@ -157,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } const ImplementsGetwd = true 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 0bd25ef81..e02d8ceae 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 f6eda2705..77081de8c 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 b6919ca58..05c95bccf 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 f6f707acf..23f39b7af 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 4596d041c..b473038c6 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 @@ -1105,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { diff --git a/vendor/golang.org/x/sys/unix/sysvshm_linux.go b/vendor/golang.org/x/sys/unix/sysvshm_linux.go index 2c3a4437f..4fcd38de2 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 5bb41d17b..79a84f18b 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 71bddefdb..9eb0db664 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 616b1b284..7997b1902 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 168d5ae77..cb7e598ce 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 f5f8e9f36..e16879396 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 ca9799b79..2fb219d78 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 200c8c26f..b0e6f5c85 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 143007627..e40fa8524 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 ab044a742..bb02aa6c0 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 17bba0e44..c0e0f8694 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 f8c2c5138..6c6923906 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 96310c3be..dd9163f8e 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 777b69def..493a2a793 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 c557ac2db..8b437b307 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 341b4d962..67c02dd57 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 f9c7f479b..c73cfe2f1 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,13 @@ 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_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 @@ -521,6 +523,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 +779,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 +1703,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 @@ -1795,6 +1801,7 @@ const ( LOCK_SH = 0x1 LOCK_UN = 0x8 LOOP_CLR_FD = 0x4c01 + LOOP_CONFIGURE = 0x4c0a LOOP_CTL_ADD = 0x4c80 LOOP_CTL_GET_FREE = 0x4c82 LOOP_CTL_REMOVE = 0x4c81 @@ -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 30aee00a5..4920821cf 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 8ebfa5127..a0c1e4112 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 271a21cdc..c63985560 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 910c330a3..47cc62e25 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 a640798c9..27ac4a09e 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 0d5925d34..54694642a 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 d72a00e0b..3adb81d75 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 02ba129f8..2dfe98f0d 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 8daa6dd96..f5398f84f 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 63c8fa2f7..c54f152d6 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 930799ec1..76057dc72 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 8605a7dd7..e0c3725e2 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 95a016f1c..18f2813ed 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 1ae0108f5..11619d4ec 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 1bb7c6333..396d994da 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 72f7420d2..130085df4 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 8d4eb0c08..84769a1a3 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 9eef9749f..602ded003 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 3b62ba192..efc0406ee 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 af20e474b..5a6500f83 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 6015fcb2b..a5aeeb979 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 8d44955e4..0e9748a72 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 ae16fe754..4f4449abc 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 03d90fe35..76a363f0f 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 8e2c51b1e..43ca0cdfd 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 13d403031..b1b8bb200 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 1afee6a08..d2ddd3176 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 fc7d0506f..4dfd2e051 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 97f20ca28..586317c78 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 0b5f79430..d7c881be7 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 2807f7e64..2d2de5d29 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 281ea64e3..5adc79fb5 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 d1d1d2331..6ea64a3c0 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 f99a18adc..99ee4399a 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 c4d50ae50..b68a78362 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 6903d3b09..0a87450bf 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 1cad561e9..ccb02f240 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 b18edbd0e..1b40b997b 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 0c67df64a..aad65fc79 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 e6e05d145..c0096391a 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 7508accac..7664df749 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 7b56aead4..ae099182c 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 cc623dcaa..11fd5d45b 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 581849197..c3d2d6530 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 6be25cd19..c698cbc01 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 1ff3aec74..1488d2712 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 @@ -38,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { @@ -2195,3 +2209,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 07b549cc2..4def3e9fc 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 5f481bf83..fef2bc8ba 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 824cd52c7..a9fd76a88 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 e77aecfe9..460065028 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 806ffd1e1..c8987d264 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 961a3afb7..921f43061 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 ed05005e9..44f067829 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 d365b718f..e7fa0abf0 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 c3f1b8bbd..8c5125675 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 a6574cf98..7392fd45e 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 f40990264..41180434e 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 9dfcc2997..40c6ce7ae 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 0ab4f2ed7..2cfe34adb 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 6cde32237..61e6f0709 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 5253d65bf..834b84204 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 2df3c5bac..e91ebc14a 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 a60556bab..be28babbc 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 9f788917a..fb587e826 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 82a4cb2dc..d576438bb 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 66b3b6456..a1d061597 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 3dcacd30d..41b561731 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,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 c5c4cc112..5b2a74097 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 2763620b0..4019a656f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,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 93bfbb328..f6eda1344 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 c92231404..ac4af24f9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,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 a107b8fda..55df20ae9 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 a6bc32c92..f77d53212 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,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 c427de509..8c1155cbc 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 b4e7bceab..fae140b62 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,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 60c1a99ae..7cc80c58d 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 ca3f76600..9d1e0ff06 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_fcntl(SB) + RET +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ppoll(SB) RET @@ -801,8 +807,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 52eba360f..0688737f4 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 @@ -585,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2213,6 +2238,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 +2269,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 477a7d5b2..da115f9a4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,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 b40189464..829b87feb 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 1d8fe1d4b..94f011238 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 55e048471..3a58ae819 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 d2243cf83..dcb7a0eb7 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 82dc51bd8..db5a7bf13 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 cbdda1a4a..7be575a77 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 f55eae1a8..d6e3174c6 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 e44054470..ee97157d0 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 a0db82fce..35c3b91d0 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 f8298ff9b..5edda7687 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 5eb433bbf..0dc9e8b4d 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 703675c0c..308ddf3a1 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 4e0d96107..418664e3d 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 01636b838..34d0b86d7 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 ad99bc106..b71cf45e2 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 89dcc4274..e32df1c1e 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 ee37aaa0c..15ad6111f 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 9862853d3..fcf3ecbdd 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 8901f0f4e..f56dc2504 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 6902c37ee..974bf2467 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 a6d3dff81..39a2739e2 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 b18f3f710..cf9c9d77e 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 0302e5e3d..10b7362ef 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 6693ba4a0..cd4d8b4fd 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 fd93f4987..2c0efca81 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 760ddcadc..a72e31d39 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 cff2b2555..c7d1e3747 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 a4b2405d0..f4d4838c8 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 aca54b4e3..b64f0e591 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 9d1738d64..95711195a 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 022878dc8..f94e943bc 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 4100a761c..ba0c2bc51 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 3a6699eba..b2aa8cd49 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 5677cd4f1..524a1b1c9 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 e784cb6db..d59b943ac 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 bd4952efa..31e771d53 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 597733813..9fd77c6cb 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 16af29189..af10af28c 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 f59b18a97..cc2028af4 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 721ef5910..c06dd4415 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 01c43a01f..9ddbf3e08 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 f258cfa24..19a6ee413 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 07919e0ec..05192a782 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 073daad43..b2e308581 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 7a8161c1d..3e6d57cae 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 07ed733c5..3a219bdce 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 690cefc3d..091d107f3 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 5bffc10ea..28ff4ef74 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 d0ba8e9b8..30e405bb4 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 29dc48337..6cbd094a3 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 0a89b2890..7c03b6ee7 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 c8666bb15..422107ee8 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 88fb48a88..505a12acf 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 698dc975e..cc986c790 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 18aa70b42..bbf8399ff 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 @@ -2672,6 +2671,7 @@ const ( BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e BPF_PROG_TYPE_SYSCALL = 0x1f + BPF_PROG_TYPE_NETFILTER = 0x20 BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2716,6 +2716,11 @@ const ( BPF_PERF_EVENT = 0x29 BPF_TRACE_KPROBE_MULTI = 0x2a BPF_LSM_CGROUP = 0x2b + BPF_STRUCT_OPS = 0x2c + BPF_NETFILTER = 0x2d + BPF_TCX_INGRESS = 0x2e + BPF_TCX_EGRESS = 0x2f + BPF_TRACE_UPROBE_MULTI = 0x30 BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2726,6 +2731,18 @@ const ( BPF_LINK_TYPE_PERF_EVENT = 0x7 BPF_LINK_TYPE_KPROBE_MULTI = 0x8 BPF_LINK_TYPE_STRUCT_OPS = 0x9 + BPF_LINK_TYPE_NETFILTER = 0xa + BPF_LINK_TYPE_TCX = 0xb + BPF_LINK_TYPE_UPROBE_MULTI = 0xc + BPF_PERF_EVENT_UNSPEC = 0x0 + BPF_PERF_EVENT_UPROBE = 0x1 + BPF_PERF_EVENT_URETPROBE = 0x2 + BPF_PERF_EVENT_KPROBE = 0x3 + BPF_PERF_EVENT_KRETPROBE = 0x4 + BPF_PERF_EVENT_TRACEPOINT = 0x5 + BPF_PERF_EVENT_EVENT = 0x6 + BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_UPROBE_MULTI_RETURN = 0x1 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2743,6 +2760,8 @@ const ( BPF_F_MMAPABLE = 0x400 BPF_F_PRESERVE_ELEMS = 0x800 BPF_F_INNER_MAP = 0x1000 + BPF_F_LINK = 0x2000 + BPF_F_PATH_FD = 0x4000 BPF_STATS_RUN_TIME = 0x0 BPF_STACK_BUILD_ID_EMPTY = 0x0 BPF_STACK_BUILD_ID_VALID = 0x1 @@ -2763,6 +2782,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_NO_TUNNEL_KEY = 0x10 BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff @@ -2779,6 +2799,8 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 + BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 0x80 + BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 0x100 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2867,6 +2889,8 @@ const ( BPF_DEVCG_DEV_CHAR = 0x2 BPF_FIB_LOOKUP_DIRECT = 0x1 BPF_FIB_LOOKUP_OUTPUT = 0x2 + BPF_FIB_LOOKUP_SKIP_NEIGH = 0x4 + BPF_FIB_LOOKUP_TBID = 0x8 BPF_FIB_LKUP_RET_SUCCESS = 0x0 BPF_FIB_LKUP_RET_BLACKHOLE = 0x1 BPF_FIB_LKUP_RET_UNREACHABLE = 0x2 @@ -2902,6 +2926,7 @@ const ( BPF_CORE_ENUMVAL_EXISTS = 0xa BPF_CORE_ENUMVAL_VALUE = 0xb BPF_CORE_TYPE_MATCHES = 0xc + BPF_F_TIMER_ABS = 0x1 ) const ( @@ -2980,6 +3005,12 @@ type LoopInfo64 struct { Encrypt_key [32]uint8 Init [2]uint64 } +type LoopConfig struct { + Fd uint32 + Size uint32 + Info LoopInfo64 + _ [8]uint64 +} type TIPCSocketAddr struct { Ref uint32 @@ -5883,3 +5914,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 6d8acbcc5..438a30aff 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 59293c688..adceca355 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 40cfa38c2..eeaa00a37 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 055bc4216..6739aa91d 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 f28affbc6..9920ef631 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 9d71e7ccd..2923b799a 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 fd5ccd332..ce2750ee4 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 7704de77a..3038811d7 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 df00b8757..efc6fed18 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 0942840db..9a654b75a 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 034874395..40d358e33 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 bad067047..148c6ceb8 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 1b4c97c32..72ba81543 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 aa268d025..71e765508 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 444045b6c..4abbdb9de 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 9bc4c8f9d..f22e7947d 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 bb05f655d..066a7d83d 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 db40e3a19..439548ec9 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 11121151c..16085d3bb 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 26eba23b7..afd13a3af 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 5a5479886..5d97f1f9b 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 be58c4e1f..34871cdc1 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 52338266c..5911bceb3 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 605cfdb12..e4f24f3bc 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 d6724c010..ca50a7930 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 ddfd27a43..d7d7f7902 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 0400747c6..14160576d 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 aec1efcb3..54f31be63 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 a20ebea63..ce2d713d6 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 fdbbbcd31..ba64caca5 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 2cd60645e..6c366955d 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 8563f79c5..dbcdb090c 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 9196b089c..0f1bdc386 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 7bae4817a..0c78da78b 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/service.go b/vendor/golang.org/x/sys/windows/service.go index c44a1b963..a9dc6308d 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 4fc01434e..6a4f9ce6a 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 8732cdb95..e85ed6b9c 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 35cfc57ca..47dc57967 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -155,6 +155,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW //sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW //sys SetDefaultDllDirectories(directoryFlags uint32) (err error) +//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory +//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory //sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW //sys GetVersion() (ver uint32, err error) //sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW @@ -233,6 +235,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 +972,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 b88dc7c85..359780f6a 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 8b1688de4..146a1f019 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -184,6 +184,7 @@ var ( procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") procCancelIoEx = modkernel32.NewProc("CancelIoEx") @@ -253,6 +254,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") @@ -329,6 +331,7 @@ var ( procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") procReleaseMutex = modkernel32.NewProc("ReleaseMutex") procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") + procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory") procResetEvent = modkernel32.NewProc("ResetEvent") procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procResumeThread = modkernel32.NewProc("ResumeThread") @@ -1604,6 +1607,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { return } +func AddDllDirectory(path *uint16) (cookie uintptr, err error) { + r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + cookie = uintptr(r0) + if cookie == 0 { + err = errnoErr(e1) + } + return +} + func AssignProcessToJobObject(job Handle, process Handle) (err error) { r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0) if r1 == 0 { @@ -2185,6 +2197,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) @@ -2870,6 +2890,14 @@ func RemoveDirectory(path *uint16) (err error) { return } +func RemoveDllDirectory(cookie uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ResetEvent(event Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0) if r1 == 0 { diff --git a/vendor/golang.org/x/term/term_unix.go b/vendor/golang.org/x/term/term_unix.go index 62c2b3f41..1ad0ddfe3 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 853b3d698..9dbf54629 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 1e8955c93..1b36de799 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 f1df85065..3c409e588 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 8a7392c4a..784bb8808 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 bb0a92001..8e1e94395 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 42fa8d72c..d2bd71181 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 56a0e1ea2..f76bdca27 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 baacf32b4..3aa2c3bdf 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 ffadb7beb..a71375790 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 92cce5802..f15746f7d 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 f517fdb20..c164d3791 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 f5a078827..1af161c75 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 cb7239c43..eb73ecc37 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 11b273300..276cb8d8c 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 f65785e8a..0cceffd73 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 e1858b879..b0819e42d 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 0175eae50..bf65457d9 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/google.golang.org/grpc/CONTRIBUTING.md b/vendor/google.golang.org/grpc/CONTRIBUTING.md index 8e001134d..608aa6e1a 100644 --- a/vendor/google.golang.org/grpc/CONTRIBUTING.md +++ b/vendor/google.golang.org/grpc/CONTRIBUTING.md @@ -20,10 +20,6 @@ How to get your contributions merged smoothly and quickly. both author's & review's time is wasted. Create more PRs to address different concerns and everyone will be happy. -- For speculative changes, consider opening an issue and discussing it first. If - you are suggesting a behavioral or API change, consider starting with a [gRFC - proposal](https://github.com/grpc/proposal). - - If you are searching for features to work on, issues labeled [Status: Help Wanted](https://github.com/grpc/grpc-go/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Status%3A+Help+Wanted%22) is a great place to start. These issues are well-documented and usually can be diff --git a/vendor/google.golang.org/grpc/attributes/attributes.go b/vendor/google.golang.org/grpc/attributes/attributes.go index 02f5dc531..3efca4591 100644 --- a/vendor/google.golang.org/grpc/attributes/attributes.go +++ b/vendor/google.golang.org/grpc/attributes/attributes.go @@ -25,6 +25,11 @@ // later release. package attributes +import ( + "fmt" + "strings" +) + // Attributes is an immutable struct for storing and retrieving generic // key/value pairs. Keys must be hashable, and users should define their own // types for keys. Values should not be modified after they are added to an @@ -99,3 +104,27 @@ func (a *Attributes) Equal(o *Attributes) bool { } return true } + +// String prints the attribute map. If any key or values throughout the map +// implement fmt.Stringer, it calls that method and appends. +func (a *Attributes) String() string { + var sb strings.Builder + sb.WriteString("{") + first := true + for k, v := range a.m { + var key, val string + if str, ok := k.(interface{ String() string }); ok { + key = str.String() + } + if str, ok := v.(interface{ String() string }); ok { + val = str.String() + } + if !first { + sb.WriteString(", ") + } + sb.WriteString(fmt.Sprintf("%q: %q, ", key, val)) + first = false + } + sb.WriteString("}") + return sb.String() +} diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index 09d61dd1b..8f00523c0 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -286,7 +286,7 @@ type PickResult struct { // // LB policies with child policies are responsible for propagating metadata // injected by their children to the ClientConn, as part of Pick(). - Metatada metadata.MD + Metadata metadata.MD } // TransientFailureError returns e. It exists for backward compatibility and diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go index 0359956d3..04b9ad411 100644 --- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go +++ b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go @@ -25,14 +25,20 @@ import ( "sync" "google.golang.org/grpc/balancer" - "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/internal/balancer/gracefulswitch" - "google.golang.org/grpc/internal/buffer" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/resolver" - "google.golang.org/grpc/status" +) + +type ccbMode int + +const ( + ccbModeActive = iota + ccbModeIdle + ccbModeClosed + ccbModeExitingIdle ) // ccBalancerWrapper sits between the ClientConn and the Balancer. @@ -49,192 +55,101 @@ import ( // It uses the gracefulswitch.Balancer internally to ensure that balancer // switches happen in a graceful manner. type ccBalancerWrapper struct { - cc *ClientConn - - // Since these fields are accessed only from handleXxx() methods which are - // synchronized by the watcher goroutine, we do not need a mutex to protect - // these fields. + // The following fields are initialized when the wrapper is created and are + // read-only afterwards, and therefore can be accessed without a mutex. + cc *ClientConn + opts balancer.BuildOptions + + // Outgoing (gRPC --> balancer) calls are guaranteed to execute in a + // mutually exclusive manner as they are scheduled in the serializer. Fields + // accessed *only* in these serializer callbacks, can therefore be accessed + // without a mutex. balancer *gracefulswitch.Balancer curBalancerName string - updateCh *buffer.Unbounded // Updates written on this channel are processed by watcher(). - resultCh *buffer.Unbounded // Results of calls to UpdateClientConnState() are pushed here. - closed *grpcsync.Event // Indicates if close has been called. - done *grpcsync.Event // Indicates if close has completed its work. + // mu guards access to the below fields. Access to the serializer and its + // cancel function needs to be mutex protected because they are overwritten + // when the wrapper exits idle mode. + mu sync.Mutex + serializer *grpcsync.CallbackSerializer // To serialize all outoing calls. + serializerCancel context.CancelFunc // To close the seralizer at close/enterIdle time. + mode ccbMode // Tracks the current mode of the wrapper. } // newCCBalancerWrapper creates a new balancer wrapper. The underlying balancer // is not created until the switchTo() method is invoked. func newCCBalancerWrapper(cc *ClientConn, bopts balancer.BuildOptions) *ccBalancerWrapper { + ctx, cancel := context.WithCancel(context.Background()) ccb := &ccBalancerWrapper{ - cc: cc, - updateCh: buffer.NewUnbounded(), - resultCh: buffer.NewUnbounded(), - closed: grpcsync.NewEvent(), - done: grpcsync.NewEvent(), + cc: cc, + opts: bopts, + serializer: grpcsync.NewCallbackSerializer(ctx), + serializerCancel: cancel, } - go ccb.watcher() ccb.balancer = gracefulswitch.NewBalancer(ccb, bopts) return ccb } -// The following xxxUpdate structs wrap the arguments received as part of the -// corresponding update. The watcher goroutine uses the 'type' of the update to -// invoke the appropriate handler routine to handle the update. - -type ccStateUpdate struct { - ccs *balancer.ClientConnState -} - -type scStateUpdate struct { - sc balancer.SubConn - state connectivity.State - err error -} - -type exitIdleUpdate struct{} - -type resolverErrorUpdate struct { - err error -} - -type switchToUpdate struct { - name string -} - -type subConnUpdate struct { - acbw *acBalancerWrapper -} - -// watcher is a long-running goroutine which reads updates from a channel and -// invokes corresponding methods on the underlying balancer. It ensures that -// these methods are invoked in a synchronous fashion. It also ensures that -// these methods are invoked in the order in which the updates were received. -func (ccb *ccBalancerWrapper) watcher() { - for { - select { - case u := <-ccb.updateCh.Get(): - ccb.updateCh.Load() - if ccb.closed.HasFired() { - break - } - switch update := u.(type) { - case *ccStateUpdate: - ccb.handleClientConnStateChange(update.ccs) - case *scStateUpdate: - ccb.handleSubConnStateChange(update) - case *exitIdleUpdate: - ccb.handleExitIdle() - case *resolverErrorUpdate: - ccb.handleResolverError(update.err) - case *switchToUpdate: - ccb.handleSwitchTo(update.name) - case *subConnUpdate: - ccb.handleRemoveSubConn(update.acbw) - default: - logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", update, update) - } - case <-ccb.closed.Done(): - } - - if ccb.closed.HasFired() { - ccb.handleClose() - return - } - } -} - // updateClientConnState is invoked by grpc to push a ClientConnState update to // the underlying balancer. -// -// Unlike other methods invoked by grpc to push updates to the underlying -// balancer, this method cannot simply push the update onto the update channel -// and return. It needs to return the error returned by the underlying balancer -// back to grpc which propagates that to the resolver. func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { - ccb.updateCh.Put(&ccStateUpdate{ccs: ccs}) - - var res interface{} - select { - case res = <-ccb.resultCh.Get(): - ccb.resultCh.Load() - case <-ccb.closed.Done(): - // Return early if the balancer wrapper is closed while we are waiting for - // the underlying balancer to process a ClientConnState update. - return nil - } - // If the returned error is nil, attempting to type assert to error leads to - // panic. So, this needs to handled separately. - if res == nil { - return nil - } - return res.(error) -} - -// handleClientConnStateChange handles a ClientConnState update from the update -// channel and invokes the appropriate method on the underlying balancer. -// -// If the addresses specified in the update contain addresses of type "grpclb" -// and the selected LB policy is not "grpclb", these addresses will be filtered -// out and ccs will be modified with the updated address list. -func (ccb *ccBalancerWrapper) handleClientConnStateChange(ccs *balancer.ClientConnState) { - if ccb.curBalancerName != grpclbName { - // Filter any grpclb addresses since we don't have the grpclb balancer. - var addrs []resolver.Address - for _, addr := range ccs.ResolverState.Addresses { - if addr.Type == resolver.GRPCLB { - continue + ccb.mu.Lock() + errCh := make(chan error, 1) + // Here and everywhere else where Schedule() is called, it is done with the + // lock held. But the lock guards only the scheduling part. The actual + // callback is called asynchronously without the lock being held. + ok := ccb.serializer.Schedule(func(_ context.Context) { + // If the addresses specified in the update contain addresses of type + // "grpclb" and the selected LB policy is not "grpclb", these addresses + // will be filtered out and ccs will be modified with the updated + // address list. + if ccb.curBalancerName != grpclbName { + var addrs []resolver.Address + for _, addr := range ccs.ResolverState.Addresses { + if addr.Type == resolver.GRPCLB { + continue + } + addrs = append(addrs, addr) } - addrs = append(addrs, addr) + ccs.ResolverState.Addresses = addrs } - ccs.ResolverState.Addresses = addrs + errCh <- ccb.balancer.UpdateClientConnState(*ccs) + }) + if !ok { + // If we are unable to schedule a function with the serializer, it + // indicates that it has been closed. A serializer is only closed when + // the wrapper is closed or is in idle. + ccb.mu.Unlock() + return fmt.Errorf("grpc: cannot send state update to a closed or idle balancer") } - ccb.resultCh.Put(ccb.balancer.UpdateClientConnState(*ccs)) + ccb.mu.Unlock() + + // We get here only if the above call to Schedule succeeds, in which case it + // is guaranteed that the scheduled function will run. Therefore it is safe + // to block on this channel. + err := <-errCh + if logger.V(2) && err != nil { + logger.Infof("error from balancer.UpdateClientConnState: %v", err) + } + return err } // updateSubConnState is invoked by grpc to push a subConn state update to the // underlying balancer. func (ccb *ccBalancerWrapper) updateSubConnState(sc balancer.SubConn, s connectivity.State, err error) { - // When updating addresses for a SubConn, if the address in use is not in - // the new addresses, the old ac will be tearDown() and a new ac will be - // created. tearDown() generates a state change with Shutdown state, we - // don't want the balancer to receive this state change. So before - // tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and - // this function will be called with (nil, Shutdown). We don't need to call - // balancer method in this case. - if sc == nil { - return - } - ccb.updateCh.Put(&scStateUpdate{ - sc: sc, - state: s, - err: err, + ccb.mu.Lock() + ccb.serializer.Schedule(func(_ context.Context) { + ccb.balancer.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: s, ConnectionError: err}) }) -} - -// handleSubConnStateChange handles a SubConnState update from the update -// channel and invokes the appropriate method on the underlying balancer. -func (ccb *ccBalancerWrapper) handleSubConnStateChange(update *scStateUpdate) { - ccb.balancer.UpdateSubConnState(update.sc, balancer.SubConnState{ConnectivityState: update.state, ConnectionError: update.err}) -} - -func (ccb *ccBalancerWrapper) exitIdle() { - ccb.updateCh.Put(&exitIdleUpdate{}) -} - -func (ccb *ccBalancerWrapper) handleExitIdle() { - if ccb.cc.GetState() != connectivity.Idle { - return - } - ccb.balancer.ExitIdle() + ccb.mu.Unlock() } func (ccb *ccBalancerWrapper) resolverError(err error) { - ccb.updateCh.Put(&resolverErrorUpdate{err: err}) -} - -func (ccb *ccBalancerWrapper) handleResolverError(err error) { - ccb.balancer.ResolverError(err) + ccb.mu.Lock() + ccb.serializer.Schedule(func(_ context.Context) { + ccb.balancer.ResolverError(err) + }) + ccb.mu.Unlock() } // switchTo is invoked by grpc to instruct the balancer wrapper to switch to the @@ -248,24 +163,27 @@ func (ccb *ccBalancerWrapper) handleResolverError(err error) { // the ccBalancerWrapper keeps track of the current LB policy name, and skips // the graceful balancer switching process if the name does not change. func (ccb *ccBalancerWrapper) switchTo(name string) { - ccb.updateCh.Put(&switchToUpdate{name: name}) + ccb.mu.Lock() + ccb.serializer.Schedule(func(_ context.Context) { + // TODO: Other languages use case-sensitive balancer registries. We should + // switch as well. See: https://github.com/grpc/grpc-go/issues/5288. + if strings.EqualFold(ccb.curBalancerName, name) { + return + } + ccb.buildLoadBalancingPolicy(name) + }) + ccb.mu.Unlock() } -// handleSwitchTo handles a balancer switch update from the update channel. It -// calls the SwitchTo() method on the gracefulswitch.Balancer with a -// balancer.Builder corresponding to name. If no balancer.Builder is registered -// for the given name, it uses the default LB policy which is "pick_first". -func (ccb *ccBalancerWrapper) handleSwitchTo(name string) { - // TODO: Other languages use case-insensitive balancer registries. We should - // switch as well. See: https://github.com/grpc/grpc-go/issues/5288. - if strings.EqualFold(ccb.curBalancerName, name) { - return - } - - // TODO: Ensure that name is a registered LB policy when we get here. - // We currently only validate the `loadBalancingConfig` field. We need to do - // the same for the `loadBalancingPolicy` field and reject the service config - // if the specified policy is not registered. +// buildLoadBalancingPolicy performs the following: +// - retrieve a balancer builder for the given name. Use the default LB +// policy, pick_first, if no LB policy with name is found in the registry. +// - instruct the gracefulswitch balancer to switch to the above builder. This +// will actually build the new balancer. +// - update the `curBalancerName` field +// +// Must be called from a serializer callback. +func (ccb *ccBalancerWrapper) buildLoadBalancingPolicy(name string) { builder := balancer.Get(name) if builder == nil { channelz.Warningf(logger, ccb.cc.channelzID, "Channel switches to new LB policy %q, since the specified LB policy %q was not registered", PickFirstBalancerName, name) @@ -281,26 +199,114 @@ func (ccb *ccBalancerWrapper) handleSwitchTo(name string) { ccb.curBalancerName = builder.Name() } -// handleRemoveSucConn handles a request from the underlying balancer to remove -// a subConn. -// -// See comments in RemoveSubConn() for more details. -func (ccb *ccBalancerWrapper) handleRemoveSubConn(acbw *acBalancerWrapper) { - ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) +func (ccb *ccBalancerWrapper) close() { + channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: closing") + ccb.closeBalancer(ccbModeClosed) } -func (ccb *ccBalancerWrapper) close() { - ccb.closed.Fire() - <-ccb.done.Done() +// enterIdleMode is invoked by grpc when the channel enters idle mode upon +// expiry of idle_timeout. This call blocks until the balancer is closed. +func (ccb *ccBalancerWrapper) enterIdleMode() { + channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: entering idle mode") + ccb.closeBalancer(ccbModeIdle) +} + +// closeBalancer is invoked when the channel is being closed or when it enters +// idle mode upon expiry of idle_timeout. +func (ccb *ccBalancerWrapper) closeBalancer(m ccbMode) { + ccb.mu.Lock() + if ccb.mode == ccbModeClosed || ccb.mode == ccbModeIdle { + ccb.mu.Unlock() + return + } + + ccb.mode = m + done := ccb.serializer.Done + b := ccb.balancer + ok := ccb.serializer.Schedule(func(_ context.Context) { + // Close the serializer to ensure that no more calls from gRPC are sent + // to the balancer. + ccb.serializerCancel() + // Empty the current balancer name because we don't have a balancer + // anymore and also so that we act on the next call to switchTo by + // creating a new balancer specified by the new resolver. + ccb.curBalancerName = "" + }) + if !ok { + ccb.mu.Unlock() + return + } + ccb.mu.Unlock() + + // Give enqueued callbacks a chance to finish. + <-done + // Spawn a goroutine to close the balancer (since it may block trying to + // cleanup all allocated resources) and return early. + go b.Close() } -func (ccb *ccBalancerWrapper) handleClose() { - ccb.balancer.Close() - ccb.done.Fire() +// exitIdleMode is invoked by grpc when the channel exits idle mode either +// because of an RPC or because of an invocation of the Connect() API. This +// recreates the balancer that was closed previously when entering idle mode. +// +// If the channel is not in idle mode, we know for a fact that we are here as a +// result of the user calling the Connect() method on the ClientConn. In this +// case, we can simply forward the call to the underlying balancer, instructing +// it to reconnect to the backends. +func (ccb *ccBalancerWrapper) exitIdleMode() { + ccb.mu.Lock() + if ccb.mode == ccbModeClosed { + // Request to exit idle is a no-op when wrapper is already closed. + ccb.mu.Unlock() + return + } + + if ccb.mode == ccbModeIdle { + // Recreate the serializer which was closed when we entered idle. + ctx, cancel := context.WithCancel(context.Background()) + ccb.serializer = grpcsync.NewCallbackSerializer(ctx) + ccb.serializerCancel = cancel + } + + // The ClientConn guarantees that mutual exclusion between close() and + // exitIdleMode(), and since we just created a new serializer, we can be + // sure that the below function will be scheduled. + done := make(chan struct{}) + ccb.serializer.Schedule(func(_ context.Context) { + defer close(done) + + ccb.mu.Lock() + defer ccb.mu.Unlock() + + if ccb.mode != ccbModeIdle { + ccb.balancer.ExitIdle() + return + } + + // Gracefulswitch balancer does not support a switchTo operation after + // being closed. Hence we need to create a new one here. + ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts) + ccb.mode = ccbModeActive + channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: exiting idle mode") + + }) + ccb.mu.Unlock() + + <-done +} + +func (ccb *ccBalancerWrapper) isIdleOrClosed() bool { + ccb.mu.Lock() + defer ccb.mu.Unlock() + return ccb.mode == ccbModeIdle || ccb.mode == ccbModeClosed } func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { - if len(addrs) <= 0 { + if ccb.isIdleOrClosed() { + return nil, fmt.Errorf("grpc: cannot create SubConn when balancer is closed or idle") + } + + if len(addrs) == 0 { return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") } ac, err := ccb.cc.newAddrConn(addrs, opts) @@ -309,31 +315,35 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer return nil, err } acbw := &acBalancerWrapper{ac: ac, producers: make(map[balancer.ProducerBuilder]*refCountedProducer)} - acbw.ac.mu.Lock() ac.acbw = acbw - acbw.ac.mu.Unlock() return acbw, nil } func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { - // Before we switched the ccBalancerWrapper to use gracefulswitch.Balancer, it - // was required to handle the RemoveSubConn() method asynchronously by pushing - // the update onto the update channel. This was done to avoid a deadlock as - // switchBalancer() was holding cc.mu when calling Close() on the old - // balancer, which would in turn call RemoveSubConn(). - // - // With the use of gracefulswitch.Balancer in ccBalancerWrapper, handling this - // asynchronously is probably not required anymore since the switchTo() method - // handles the balancer switch by pushing the update onto the channel. - // TODO(easwars): Handle this inline. + if ccb.isIdleOrClosed() { + // It it safe to ignore this call when the balancer is closed or in idle + // because the ClientConn takes care of closing the connections. + // + // Not returning early from here when the balancer is closed or in idle + // leads to a deadlock though, because of the following sequence of + // calls when holding cc.mu: + // cc.exitIdleMode --> ccb.enterIdleMode --> gsw.Close --> + // ccb.RemoveAddrConn --> cc.removeAddrConn + return + } + acbw, ok := sc.(*acBalancerWrapper) if !ok { return } - ccb.updateCh.Put(&subConnUpdate{acbw: acbw}) + ccb.cc.removeAddrConn(acbw.ac, errConnDrain) } func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { + if ccb.isIdleOrClosed() { + return + } + acbw, ok := sc.(*acBalancerWrapper) if !ok { return @@ -342,6 +352,10 @@ func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resol } func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { + if ccb.isIdleOrClosed() { + return + } + // Update picker before updating state. Even though the ordering here does // not matter, it can lead to multiple calls of Pick in the common start-up // case where we wait for ready and then perform an RPC. If the picker is @@ -352,6 +366,10 @@ func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { } func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) { + if ccb.isIdleOrClosed() { + return + } + ccb.cc.resolveNow(o) } @@ -362,71 +380,31 @@ func (ccb *ccBalancerWrapper) Target() string { // acBalancerWrapper is a wrapper on top of ac for balancers. // It implements balancer.SubConn interface. type acBalancerWrapper struct { + ac *addrConn // read-only + mu sync.Mutex - ac *addrConn producers map[balancer.ProducerBuilder]*refCountedProducer } -func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { - acbw.mu.Lock() - defer acbw.mu.Unlock() - if len(addrs) <= 0 { - acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain) - return - } - if !acbw.ac.tryUpdateAddrs(addrs) { - cc := acbw.ac.cc - opts := acbw.ac.scopts - acbw.ac.mu.Lock() - // Set old ac.acbw to nil so the Shutdown state update will be ignored - // by balancer. - // - // TODO(bar) the state transition could be wrong when tearDown() old ac - // and creating new ac, fix the transition. - acbw.ac.acbw = nil - acbw.ac.mu.Unlock() - acState := acbw.ac.getState() - acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain) - - if acState == connectivity.Shutdown { - return - } +func (acbw *acBalancerWrapper) String() string { + return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelzID.Int()) +} - newAC, err := cc.newAddrConn(addrs, opts) - if err != nil { - channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err) - return - } - acbw.ac = newAC - newAC.mu.Lock() - newAC.acbw = acbw - newAC.mu.Unlock() - if acState != connectivity.Idle { - go newAC.connect() - } - } +func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { + acbw.ac.updateAddrs(addrs) } func (acbw *acBalancerWrapper) Connect() { - acbw.mu.Lock() - defer acbw.mu.Unlock() go acbw.ac.connect() } -func (acbw *acBalancerWrapper) getAddrConn() *addrConn { - acbw.mu.Lock() - defer acbw.mu.Unlock() - return acbw.ac -} - -var errSubConnNotReady = status.Error(codes.Unavailable, "SubConn not currently connected") - // NewStream begins a streaming RPC on the addrConn. If the addrConn is not -// ready, returns errSubConnNotReady. +// ready, blocks until it is or ctx expires. Returns an error when the context +// expires or the addrConn is shut down. func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { - transport := acbw.ac.getReadyTransport() - if transport == nil { - return nil, errSubConnNotReady + transport, err := acbw.ac.getTransport(ctx) + if err != nil { + return nil, err } return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...) } diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go index 8cd89dab9..ec2c2fa14 100644 --- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go +++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.30.0 // protoc v4.22.0 // source: grpc/binlog/v1/binarylog.proto diff --git a/vendor/google.golang.org/grpc/call.go b/vendor/google.golang.org/grpc/call.go index 9e20e4d38..e6a1dc5d7 100644 --- a/vendor/google.golang.org/grpc/call.go +++ b/vendor/google.golang.org/grpc/call.go @@ -27,6 +27,11 @@ import ( // // All errors returned by Invoke are compatible with the status package. func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error { + if err := cc.idlenessMgr.onCallBegin(); err != nil { + return err + } + defer cc.idlenessMgr.onCallEnd() + // allow interceptor to see all applicable call options, which means those // configured as defaults from dial option as well as per-call options opts = combine(cc.dopts.callOptions, opts) diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index b9cc05507..95a7459b0 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -24,7 +24,6 @@ import ( "fmt" "math" "net/url" - "reflect" "strings" "sync" "sync/atomic" @@ -69,6 +68,9 @@ var ( errConnDrain = errors.New("grpc: the connection is drained") // errConnClosing indicates that the connection is closing. errConnClosing = errors.New("grpc: the connection is closing") + // errConnIdling indicates the the connection is being closed as the channel + // is moving to an idle mode due to inactivity. + errConnIdling = errors.New("grpc: the connection is closing due to channel idleness") // invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default // service config. invalidDefaultServiceConfigErrPrefix = "grpc: the provided default service config is invalid" @@ -134,17 +136,29 @@ func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*ires // e.g. to use dns resolver, a "dns:///" prefix should be applied to the target. func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { cc := &ClientConn{ - target: target, - csMgr: &connectivityStateManager{}, - conns: make(map[*addrConn]struct{}), - dopts: defaultDialOptions(), - blockingpicker: newPickerWrapper(), - czData: new(channelzData), - firstResolveEvent: grpcsync.NewEvent(), - } + target: target, + csMgr: &connectivityStateManager{}, + conns: make(map[*addrConn]struct{}), + dopts: defaultDialOptions(), + czData: new(channelzData), + } + + // We start the channel off in idle mode, but kick it out of idle at the end + // of this method, instead of waiting for the first RPC. Other gRPC + // implementations do wait for the first RPC to kick the channel out of + // idle. But doing so would be a major behavior change for our users who are + // used to seeing the channel active after Dial. + // + // Taking this approach of kicking it out of idle at the end of this method + // allows us to share the code between channel creation and exiting idle + // mode. This will also make it easy for us to switch to starting the + // channel off in idle, if at all we ever get to do that. + cc.idlenessState = ccIdlenessStateIdle + cc.retryThrottler.Store((*retryThrottler)(nil)) cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) cc.ctx, cc.cancel = context.WithCancel(context.Background()) + cc.exitIdleCond = sync.NewCond(&cc.mu) disableGlobalOpts := false for _, opt := range opts { @@ -173,40 +187,11 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * } }() - pid := cc.dopts.channelzParentID - cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, pid, target) - ted := &channelz.TraceEventDesc{ - Desc: "Channel created", - Severity: channelz.CtInfo, - } - if cc.dopts.channelzParentID != nil { - ted.Parent = &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Nested Channel(id:%d) created", cc.channelzID.Int()), - Severity: channelz.CtInfo, - } - } - channelz.AddTraceEvent(logger, cc.channelzID, 1, ted) - cc.csMgr.channelzID = cc.channelzID + // Register ClientConn with channelz. + cc.channelzRegistration(target) - if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil { - return nil, errNoTransportSecurity - } - if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil { - return nil, errTransportCredsAndBundle - } - if cc.dopts.copts.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil { - return nil, errNoTransportCredsInBundle - } - transportCreds := cc.dopts.copts.TransportCredentials - if transportCreds == nil { - transportCreds = cc.dopts.copts.CredsBundle.TransportCredentials() - } - if transportCreds.Info().SecurityProtocol == "insecure" { - for _, cd := range cc.dopts.copts.PerRPCCredentials { - if cd.RequireTransportSecurity() { - return nil, errTransportCredentialsMissing - } - } + if err := cc.validateTransportCredentials(); err != nil { + return nil, err } if cc.dopts.defaultServiceConfigRawJSON != nil { @@ -244,35 +229,19 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * } }() - scSet := false - if cc.dopts.scChan != nil { - // Try to get an initial service config. - select { - case sc, ok := <-cc.dopts.scChan: - if ok { - cc.sc = &sc - cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc}) - scSet = true - } - default: - } - } if cc.dopts.bs == nil { cc.dopts.bs = backoff.DefaultExponential } // Determine the resolver to use. - resolverBuilder, err := cc.parseTargetAndFindResolver() - if err != nil { + if err := cc.parseTargetAndFindResolver(); err != nil { return nil, err } - cc.authority, err = determineAuthority(cc.parsedTarget.Endpoint(), cc.target, cc.dopts) - if err != nil { + if err = cc.determineAuthority(); err != nil { return nil, err } - channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority) - if cc.dopts.scChan != nil && !scSet { + if cc.dopts.scChan != nil { // Blocking wait for the initial service config. select { case sc, ok := <-cc.dopts.scChan: @@ -288,57 +257,224 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * go cc.scWatcher() } + // This creates the name resolver, load balancer, blocking picker etc. + if err := cc.exitIdleMode(); err != nil { + return nil, err + } + + // Configure idleness support with configured idle timeout or default idle + // timeout duration. Idleness can be explicitly disabled by the user, by + // setting the dial option to 0. + cc.idlenessMgr = newIdlenessManager(cc, cc.dopts.idleTimeout) + + // Return early for non-blocking dials. + if !cc.dopts.block { + return cc, nil + } + + // A blocking dial blocks until the clientConn is ready. + for { + s := cc.GetState() + if s == connectivity.Idle { + cc.Connect() + } + if s == connectivity.Ready { + return cc, nil + } else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure { + if err = cc.connectionError(); err != nil { + terr, ok := err.(interface { + Temporary() bool + }) + if ok && !terr.Temporary() { + return nil, err + } + } + } + if !cc.WaitForStateChange(ctx, s) { + // ctx got timeout or canceled. + if err = cc.connectionError(); err != nil && cc.dopts.returnLastError { + return nil, err + } + return nil, ctx.Err() + } + } +} + +// addTraceEvent is a helper method to add a trace event on the channel. If the +// channel is a nested one, the same event is also added on the parent channel. +func (cc *ClientConn) addTraceEvent(msg string) { + ted := &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Channel %s", msg), + Severity: channelz.CtInfo, + } + if cc.dopts.channelzParentID != nil { + ted.Parent = &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Nested channel(id:%d) %s", cc.channelzID.Int(), msg), + Severity: channelz.CtInfo, + } + } + channelz.AddTraceEvent(logger, cc.channelzID, 0, ted) +} + +// exitIdleMode moves the channel out of idle mode by recreating the name +// resolver and load balancer. +func (cc *ClientConn) exitIdleMode() error { + cc.mu.Lock() + if cc.conns == nil { + cc.mu.Unlock() + return errConnClosing + } + if cc.idlenessState != ccIdlenessStateIdle { + cc.mu.Unlock() + logger.Info("ClientConn asked to exit idle mode when not in idle mode") + return nil + } + + defer func() { + // When Close() and exitIdleMode() race against each other, one of the + // following two can happen: + // - Close() wins the race and runs first. exitIdleMode() runs after, and + // sees that the ClientConn is already closed and hence returns early. + // - exitIdleMode() wins the race and runs first and recreates the balancer + // and releases the lock before recreating the resolver. If Close() runs + // in this window, it will wait for exitIdleMode to complete. + // + // We achieve this synchronization using the below condition variable. + cc.mu.Lock() + cc.idlenessState = ccIdlenessStateActive + cc.exitIdleCond.Signal() + cc.mu.Unlock() + }() + + cc.idlenessState = ccIdlenessStateExitingIdle + exitedIdle := false + if cc.blockingpicker == nil { + cc.blockingpicker = newPickerWrapper() + } else { + cc.blockingpicker.exitIdleMode() + exitedIdle = true + } + var credsClone credentials.TransportCredentials if creds := cc.dopts.copts.TransportCredentials; creds != nil { credsClone = creds.Clone() } - cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{ - DialCreds: credsClone, - CredsBundle: cc.dopts.copts.CredsBundle, - Dialer: cc.dopts.copts.Dialer, - Authority: cc.authority, - CustomUserAgent: cc.dopts.copts.UserAgent, - ChannelzParentID: cc.channelzID, - Target: cc.parsedTarget, - }) + if cc.balancerWrapper == nil { + cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{ + DialCreds: credsClone, + CredsBundle: cc.dopts.copts.CredsBundle, + Dialer: cc.dopts.copts.Dialer, + Authority: cc.authority, + CustomUserAgent: cc.dopts.copts.UserAgent, + ChannelzParentID: cc.channelzID, + Target: cc.parsedTarget, + }) + } else { + cc.balancerWrapper.exitIdleMode() + } + cc.firstResolveEvent = grpcsync.NewEvent() + cc.mu.Unlock() - // Build the resolver. - rWrapper, err := newCCResolverWrapper(cc, resolverBuilder) - if err != nil { - return nil, fmt.Errorf("failed to build resolver: %v", err) + // This needs to be called without cc.mu because this builds a new resolver + // which might update state or report error inline which needs to be handled + // by cc.updateResolverState() which also grabs cc.mu. + if err := cc.initResolverWrapper(credsClone); err != nil { + return err } + + if exitedIdle { + cc.addTraceEvent("exiting idle mode") + } + return nil +} + +// enterIdleMode puts the channel in idle mode, and as part of it shuts down the +// name resolver, load balancer and any subchannels. +func (cc *ClientConn) enterIdleMode() error { cc.mu.Lock() - cc.resolverWrapper = rWrapper + if cc.conns == nil { + cc.mu.Unlock() + return ErrClientConnClosing + } + if cc.idlenessState != ccIdlenessStateActive { + logger.Error("ClientConn asked to enter idle mode when not active") + return nil + } + + // cc.conns == nil is a proxy for the ClientConn being closed. So, instead + // of setting it to nil here, we recreate the map. This also means that we + // don't have to do this when exiting idle mode. + conns := cc.conns + cc.conns = make(map[*addrConn]struct{}) + + // TODO: Currently, we close the resolver wrapper upon entering idle mode + // and create a new one upon exiting idle mode. This means that the + // `cc.resolverWrapper` field would be overwritten everytime we exit idle + // mode. While this means that we need to hold `cc.mu` when accessing + // `cc.resolverWrapper`, it makes the code simpler in the wrapper. We should + // try to do the same for the balancer and picker wrappers too. + cc.resolverWrapper.close() + cc.blockingpicker.enterIdleMode() + cc.balancerWrapper.enterIdleMode() + cc.csMgr.updateState(connectivity.Idle) + cc.idlenessState = ccIdlenessStateIdle cc.mu.Unlock() - // A blocking dial blocks until the clientConn is ready. - if cc.dopts.block { - for { - cc.Connect() - s := cc.GetState() - if s == connectivity.Ready { - break - } else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure { - if err = cc.connectionError(); err != nil { - terr, ok := err.(interface { - Temporary() bool - }) - if ok && !terr.Temporary() { - return nil, err - } - } - } - if !cc.WaitForStateChange(ctx, s) { - // ctx got timeout or canceled. - if err = cc.connectionError(); err != nil && cc.dopts.returnLastError { - return nil, err - } - return nil, ctx.Err() + go func() { + cc.addTraceEvent("entering idle mode") + for ac := range conns { + ac.tearDown(errConnIdling) + } + }() + return nil +} + +// validateTransportCredentials performs a series of checks on the configured +// transport credentials. It returns a non-nil error if any of these conditions +// are met: +// - no transport creds and no creds bundle is configured +// - both transport creds and creds bundle are configured +// - creds bundle is configured, but it lacks a transport credentials +// - insecure transport creds configured alongside call creds that require +// transport level security +// +// If none of the above conditions are met, the configured credentials are +// deemed valid and a nil error is returned. +func (cc *ClientConn) validateTransportCredentials() error { + if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil { + return errNoTransportSecurity + } + if cc.dopts.copts.TransportCredentials != nil && cc.dopts.copts.CredsBundle != nil { + return errTransportCredsAndBundle + } + if cc.dopts.copts.CredsBundle != nil && cc.dopts.copts.CredsBundle.TransportCredentials() == nil { + return errNoTransportCredsInBundle + } + transportCreds := cc.dopts.copts.TransportCredentials + if transportCreds == nil { + transportCreds = cc.dopts.copts.CredsBundle.TransportCredentials() + } + if transportCreds.Info().SecurityProtocol == "insecure" { + for _, cd := range cc.dopts.copts.PerRPCCredentials { + if cd.RequireTransportSecurity() { + return errTransportCredentialsMissing } } } + return nil +} - return cc, nil +// channelzRegistration registers the newly created ClientConn with channelz and +// stores the returned identifier in `cc.channelzID` and `cc.csMgr.channelzID`. +// A channelz trace event is emitted for ClientConn creation. If the newly +// created ClientConn is a nested one, i.e a valid parent ClientConn ID is +// specified via a dial option, the trace event is also added to the parent. +// +// Doesn't grab cc.mu as this method is expected to be called only at Dial time. +func (cc *ClientConn) channelzRegistration(target string) { + cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target) + cc.addTraceEvent("created") + cc.csMgr.channelzID = cc.channelzID } // chainUnaryClientInterceptors chains all unary client interceptors into one. @@ -484,7 +620,9 @@ type ClientConn struct { authority string // See determineAuthority(). dopts dialOptions // Default and user specified dial options. channelzID *channelz.Identifier // Channelz identifier for the channel. + resolverBuilder resolver.Builder // See parseTargetAndFindResolver(). balancerWrapper *ccBalancerWrapper // Uses gracefulswitch.balancer underneath. + idlenessMgr idlenessManager // The following provide their own synchronization, and therefore don't // require cc.mu to be held to access them. @@ -505,11 +643,31 @@ type ClientConn struct { sc *ServiceConfig // Latest service config received from the resolver. conns map[*addrConn]struct{} // Set to nil on close. mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway. + idlenessState ccIdlenessState // Tracks idleness state of the channel. + exitIdleCond *sync.Cond // Signalled when channel exits idle. lceMu sync.Mutex // protects lastConnectionError lastConnectionError error } +// ccIdlenessState tracks the idleness state of the channel. +// +// Channels start off in `active` and move to `idle` after a period of +// inactivity. When moving back to `active` upon an incoming RPC, they +// transition through `exiting_idle`. This state is useful for synchronization +// with Close(). +// +// This state tracking is mostly for self-protection. The idlenessManager is +// expected to keep track of the state as well, and is expected not to call into +// the ClientConn unnecessarily. +type ccIdlenessState int8 + +const ( + ccIdlenessStateActive ccIdlenessState = iota + ccIdlenessStateIdle + ccIdlenessStateExitingIdle +) + // WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or // ctx expires. A true value is returned in former case and false in latter. // @@ -549,7 +707,10 @@ func (cc *ClientConn) GetState() connectivity.State { // Notice: This API is EXPERIMENTAL and may be changed or removed in a later // release. func (cc *ClientConn) Connect() { - cc.balancerWrapper.exitIdle() + cc.exitIdleMode() + // If the ClientConn was not in idle mode, we need to call ExitIdle on the + // LB policy so that connections can be created. + cc.balancerWrapper.exitIdleMode() } func (cc *ClientConn) scWatcher() { @@ -718,6 +879,7 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub dopts: cc.dopts, czData: new(channelzData), resetBackoff: make(chan struct{}), + stateChan: make(chan struct{}), } ac.ctx, ac.cancel = context.WithCancel(cc.ctx) // Track ac in cc. This needs to be done before any getTransport(...) is called. @@ -811,9 +973,6 @@ func (ac *addrConn) connect() error { ac.mu.Unlock() return nil } - // Update connectivity state within the lock to prevent subsequent or - // concurrent calls from resetting the transport more than once. - ac.updateConnectivityState(connectivity.Connecting, nil) ac.mu.Unlock() ac.resetTransport() @@ -832,58 +991,62 @@ func equalAddresses(a, b []resolver.Address) bool { return true } -// tryUpdateAddrs tries to update ac.addrs with the new addresses list. -// -// If ac is TransientFailure, it updates ac.addrs and returns true. The updated -// addresses will be picked up by retry in the next iteration after backoff. -// -// If ac is Shutdown or Idle, it updates ac.addrs and returns true. -// -// If the addresses is the same as the old list, it does nothing and returns -// true. -// -// If ac is Connecting, it returns false. The caller should tear down the ac and -// create a new one. Note that the backoff will be reset when this happens. -// -// If ac is Ready, it checks whether current connected address of ac is in the -// new addrs list. -// - If true, it updates ac.addrs and returns true. The ac will keep using -// the existing connection. -// - If false, it does nothing and returns false. -func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool { +// updateAddrs updates ac.addrs with the new addresses list and handles active +// connections or connection attempts. +func (ac *addrConn) updateAddrs(addrs []resolver.Address) { ac.mu.Lock() - defer ac.mu.Unlock() - channelz.Infof(logger, ac.channelzID, "addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs) + channelz.Infof(logger, ac.channelzID, "addrConn: updateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs) + + if equalAddresses(ac.addrs, addrs) { + ac.mu.Unlock() + return + } + + ac.addrs = addrs + if ac.state == connectivity.Shutdown || ac.state == connectivity.TransientFailure || ac.state == connectivity.Idle { - ac.addrs = addrs - return true + // We were not connecting, so do nothing but update the addresses. + ac.mu.Unlock() + return } - if equalAddresses(ac.addrs, addrs) { - return true + if ac.state == connectivity.Ready { + // Try to find the connected address. + for _, a := range addrs { + a.ServerName = ac.cc.getServerName(a) + if a.Equal(ac.curAddr) { + // We are connected to a valid address, so do nothing but + // update the addresses. + ac.mu.Unlock() + return + } + } } - if ac.state == connectivity.Connecting { - return false - } + // We are either connected to the wrong address or currently connecting. + // Stop the current iteration and restart. - // ac.state is Ready, try to find the connected address. - var curAddrFound bool - for _, a := range addrs { - a.ServerName = ac.cc.getServerName(a) - if reflect.DeepEqual(ac.curAddr, a) { - curAddrFound = true - break - } + ac.cancel() + ac.ctx, ac.cancel = context.WithCancel(ac.cc.ctx) + + // We have to defer here because GracefulClose => Close => onClose, which + // requires locking ac.mu. + if ac.transport != nil { + defer ac.transport.GracefulClose() + ac.transport = nil } - channelz.Infof(logger, ac.channelzID, "addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound) - if curAddrFound { - ac.addrs = addrs + + if len(addrs) == 0 { + ac.updateConnectivityState(connectivity.Idle, nil) } - return curAddrFound + ac.mu.Unlock() + + // Since we were connecting/connected, we should start a new connection + // attempt. + go ac.resetTransport() } // getServerName determines the serverName to be used in the connection @@ -1036,39 +1199,40 @@ func (cc *ClientConn) Close() error { cc.mu.Unlock() return ErrClientConnClosing } + + for cc.idlenessState == ccIdlenessStateExitingIdle { + cc.exitIdleCond.Wait() + } + conns := cc.conns cc.conns = nil cc.csMgr.updateState(connectivity.Shutdown) + pWrapper := cc.blockingpicker rWrapper := cc.resolverWrapper - cc.resolverWrapper = nil bWrapper := cc.balancerWrapper + idlenessMgr := cc.idlenessMgr cc.mu.Unlock() // The order of closing matters here since the balancer wrapper assumes the // picker is closed before it is closed. - cc.blockingpicker.close() + if pWrapper != nil { + pWrapper.close() + } if bWrapper != nil { bWrapper.close() } if rWrapper != nil { rWrapper.close() } + if idlenessMgr != nil { + idlenessMgr.close() + } for ac := range conns { ac.tearDown(ErrClientConnClosing) } - ted := &channelz.TraceEventDesc{ - Desc: "Channel deleted", - Severity: channelz.CtInfo, - } - if cc.dopts.channelzParentID != nil { - ted.Parent = &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Nested channel(id:%d) deleted", cc.channelzID.Int()), - Severity: channelz.CtInfo, - } - } - channelz.AddTraceEvent(logger, cc.channelzID, 0, ted) + cc.addTraceEvent("deleted") // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add // trace reference to the entity being deleted, and thus prevent it from being // deleted right away. @@ -1098,7 +1262,8 @@ type addrConn struct { addrs []resolver.Address // All addresses that the resolver resolved to. // Use updateConnectivityState for updating addrConn's connectivity state. - state connectivity.State + state connectivity.State + stateChan chan struct{} // closed and recreated on every state change. backoffIdx int // Needs to be stateful for resetConnectBackoff. resetBackoff chan struct{} @@ -1112,6 +1277,9 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) if ac.state == s { return } + // When changing states, reset the state change channel. + close(ac.stateChan) + ac.stateChan = make(chan struct{}) ac.state = s if lastErr == nil { channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v", s) @@ -1137,7 +1305,8 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) { func (ac *addrConn) resetTransport() { ac.mu.Lock() - if ac.state == connectivity.Shutdown { + acCtx := ac.ctx + if acCtx.Err() != nil { ac.mu.Unlock() return } @@ -1165,15 +1334,14 @@ func (ac *addrConn) resetTransport() { ac.updateConnectivityState(connectivity.Connecting, nil) ac.mu.Unlock() - if err := ac.tryAllAddrs(addrs, connectDeadline); err != nil { + if err := ac.tryAllAddrs(acCtx, addrs, connectDeadline); err != nil { ac.cc.resolveNow(resolver.ResolveNowOptions{}) // After exhausting all addresses, the addrConn enters // TRANSIENT_FAILURE. - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() + if acCtx.Err() != nil { return } + ac.mu.Lock() ac.updateConnectivityState(connectivity.TransientFailure, err) // Backoff. @@ -1188,13 +1356,13 @@ func (ac *addrConn) resetTransport() { ac.mu.Unlock() case <-b: timer.Stop() - case <-ac.ctx.Done(): + case <-acCtx.Done(): timer.Stop() return } ac.mu.Lock() - if ac.state != connectivity.Shutdown { + if acCtx.Err() == nil { ac.updateConnectivityState(connectivity.Idle, err) } ac.mu.Unlock() @@ -1209,14 +1377,13 @@ func (ac *addrConn) resetTransport() { // tryAllAddrs tries to creates a connection to the addresses, and stop when at // the first successful one. It returns an error if no address was successfully // connected, or updates ac appropriately with the new transport. -func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) error { +func (ac *addrConn) tryAllAddrs(ctx context.Context, addrs []resolver.Address, connectDeadline time.Time) error { var firstConnErr error for _, addr := range addrs { - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() + if ctx.Err() != nil { return errConnClosing } + ac.mu.Lock() ac.cc.mu.RLock() ac.dopts.copts.KeepaliveParams = ac.cc.mkp @@ -1230,7 +1397,7 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T channelz.Infof(logger, ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr) - err := ac.createTransport(addr, copts, connectDeadline) + err := ac.createTransport(ctx, addr, copts, connectDeadline) if err == nil { return nil } @@ -1247,19 +1414,20 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T // createTransport creates a connection to addr. It returns an error if the // address was not successfully connected, or updates ac appropriately with the // new transport. -func (ac *addrConn) createTransport(addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error { +func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error { addr.ServerName = ac.cc.getServerName(addr) - hctx, hcancel := context.WithCancel(ac.ctx) + hctx, hcancel := context.WithCancel(ctx) onClose := func(r transport.GoAwayReason) { ac.mu.Lock() defer ac.mu.Unlock() // adjust params based on GoAwayReason ac.adjustParams(r) - if ac.state == connectivity.Shutdown { - // Already shut down. tearDown() already cleared the transport and - // canceled hctx via ac.ctx, and we expected this connection to be - // closed, so do nothing here. + if ctx.Err() != nil { + // Already shut down or connection attempt canceled. tearDown() or + // updateAddrs() already cleared the transport and canceled hctx + // via ac.ctx, and we expected this connection to be closed, so do + // nothing here. return } hcancel() @@ -1278,7 +1446,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne ac.updateConnectivityState(connectivity.Idle, nil) } - connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline) + connectCtx, cancel := context.WithDeadline(ctx, connectDeadline) defer cancel() copts.ChannelzParentID = ac.channelzID @@ -1295,7 +1463,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne ac.mu.Lock() defer ac.mu.Unlock() - if ac.state == connectivity.Shutdown { + if ctx.Err() != nil { // This can happen if the subConn was removed while in `Connecting` // state. tearDown() would have set the state to `Shutdown`, but // would not have closed the transport since ac.transport would not @@ -1307,6 +1475,9 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne // The error we pass to Close() is immaterial since there are no open // streams at this point, so no trailers with error details will be sent // out. We just need to pass a non-nil error. + // + // This can also happen when updateAddrs is called during a connection + // attempt. go newTr.Close(transport.ErrConnClosing) return nil } @@ -1414,6 +1585,29 @@ func (ac *addrConn) getReadyTransport() transport.ClientTransport { return nil } +// getTransport waits until the addrconn is ready and returns the transport. +// If the context expires first, returns an appropriate status. If the +// addrConn is stopped first, returns an Unavailable status error. +func (ac *addrConn) getTransport(ctx context.Context) (transport.ClientTransport, error) { + for ctx.Err() == nil { + ac.mu.Lock() + t, state, sc := ac.transport, ac.state, ac.stateChan + ac.mu.Unlock() + if state == connectivity.Ready { + return t, nil + } + if state == connectivity.Shutdown { + return nil, status.Errorf(codes.Unavailable, "SubConn shutting down") + } + + select { + case <-ctx.Done(): + case <-sc: + } + } + return nil, status.FromContextError(ctx.Err()).Err() +} + // tearDown starts to tear down the addrConn. // // Note that tearDown doesn't remove ac from ac.cc.conns, so the addrConn struct @@ -1565,7 +1759,14 @@ func (cc *ClientConn) connectionError() error { return cc.lastConnectionError } -func (cc *ClientConn) parseTargetAndFindResolver() (resolver.Builder, error) { +// parseTargetAndFindResolver parses the user's dial target and stores the +// parsed target in `cc.parsedTarget`. +// +// The resolver to use is determined based on the scheme in the parsed target +// and the same is stored in `cc.resolverBuilder`. +// +// Doesn't grab cc.mu as this method is expected to be called only at Dial time. +func (cc *ClientConn) parseTargetAndFindResolver() error { channelz.Infof(logger, cc.channelzID, "original dial target is: %q", cc.target) var rb resolver.Builder @@ -1577,7 +1778,8 @@ func (cc *ClientConn) parseTargetAndFindResolver() (resolver.Builder, error) { rb = cc.getResolver(parsedTarget.URL.Scheme) if rb != nil { cc.parsedTarget = parsedTarget - return rb, nil + cc.resolverBuilder = rb + return nil } } @@ -1592,15 +1794,16 @@ func (cc *ClientConn) parseTargetAndFindResolver() (resolver.Builder, error) { parsedTarget, err = parseTarget(canonicalTarget) if err != nil { channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", canonicalTarget, err) - return nil, err + return err } channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget) rb = cc.getResolver(parsedTarget.URL.Scheme) if rb == nil { - return nil, fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme) + return fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme) } cc.parsedTarget = parsedTarget - return rb, nil + cc.resolverBuilder = rb + return nil } // parseTarget uses RFC 3986 semantics to parse the given target into a @@ -1623,7 +1826,15 @@ func parseTarget(target string) (resolver.Target, error) { // - user specified authority override using `WithAuthority` dial option // - creds' notion of server name for the authentication handshake // - endpoint from dial target of the form "scheme://[authority]/endpoint" -func determineAuthority(endpoint, target string, dopts dialOptions) (string, error) { +// +// Stores the determined authority in `cc.authority`. +// +// Returns a non-nil error if the authority returned by the transport +// credentials do not match the authority configured through the dial option. +// +// Doesn't grab cc.mu as this method is expected to be called only at Dial time. +func (cc *ClientConn) determineAuthority() error { + dopts := cc.dopts // Historically, we had two options for users to specify the serverName or // authority for a channel. One was through the transport credentials // (either in its constructor, or through the OverrideServerName() method). @@ -1640,25 +1851,58 @@ func determineAuthority(endpoint, target string, dopts dialOptions) (string, err } authorityFromDialOption := dopts.authority if (authorityFromCreds != "" && authorityFromDialOption != "") && authorityFromCreds != authorityFromDialOption { - return "", fmt.Errorf("ClientConn's authority from transport creds %q and dial option %q don't match", authorityFromCreds, authorityFromDialOption) + return fmt.Errorf("ClientConn's authority from transport creds %q and dial option %q don't match", authorityFromCreds, authorityFromDialOption) } + endpoint := cc.parsedTarget.Endpoint() + target := cc.target switch { case authorityFromDialOption != "": - return authorityFromDialOption, nil + cc.authority = authorityFromDialOption case authorityFromCreds != "": - return authorityFromCreds, nil + cc.authority = authorityFromCreds case strings.HasPrefix(target, "unix:") || strings.HasPrefix(target, "unix-abstract:"): // TODO: remove when the unix resolver implements optional interface to // return channel authority. - return "localhost", nil + cc.authority = "localhost" case strings.HasPrefix(endpoint, ":"): - return "localhost" + endpoint, nil + cc.authority = "localhost" + endpoint default: // TODO: Define an optional interface on the resolver builder to return // the channel authority given the user's dial target. For resolvers // which don't implement this interface, we will use the endpoint from // "scheme://authority/endpoint" as the default authority. - return endpoint, nil + cc.authority = endpoint + } + channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority) + return nil +} + +// initResolverWrapper creates a ccResolverWrapper, which builds the name +// resolver. This method grabs the lock to assign the newly built resolver +// wrapper to the cc.resolverWrapper field. +func (cc *ClientConn) initResolverWrapper(creds credentials.TransportCredentials) error { + rw, err := newCCResolverWrapper(cc, ccResolverWrapperOpts{ + target: cc.parsedTarget, + builder: cc.resolverBuilder, + bOpts: resolver.BuildOptions{ + DisableServiceConfig: cc.dopts.disableServiceConfig, + DialCreds: creds, + CredsBundle: cc.dopts.copts.CredsBundle, + Dialer: cc.dopts.copts.Dialer, + }, + channelzID: cc.channelzID, + }) + if err != nil { + return fmt.Errorf("failed to build resolver: %v", err) } + // Resolver implementations may report state update or error inline when + // built (or right after), and this is handled in cc.updateResolverState. + // Also, an error from the resolver might lead to a re-resolution request + // from the balancer, which is handled in resolveNow() where + // `cc.resolverWrapper` is accessed. Hence, we need to hold the lock here. + cc.mu.Lock() + cc.resolverWrapper = rw + cc.mu.Unlock() + return nil } diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index e9d6852fd..15a3d5102 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -77,6 +77,7 @@ type dialOptions struct { defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON. defaultServiceConfigRawJSON *string resolvers []resolver.Builder + idleTimeout time.Duration } // DialOption configures how we set up the connection. @@ -295,6 +296,9 @@ func withBackoff(bs internalbackoff.Strategy) DialOption { // WithBlock returns a DialOption which makes callers of Dial block until the // underlying connection is up. Without this, Dial returns immediately and // connecting the server happens in background. +// +// Use of this feature is not recommended. For more information, please see: +// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md func WithBlock() DialOption { return newFuncDialOption(func(o *dialOptions) { o.block = true @@ -306,6 +310,9 @@ func WithBlock() DialOption { // the context.DeadlineExceeded error. // Implies WithBlock() // +// Use of this feature is not recommended. For more information, please see: +// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md +// // # Experimental // // Notice: This API is EXPERIMENTAL and may be changed or removed in a @@ -448,6 +455,9 @@ func withBinaryLogger(bl binarylog.Logger) DialOption { // FailOnNonTempDialError only affects the initial dial, and does not do // anything useful unless you are also using WithBlock(). // +// Use of this feature is not recommended. For more information, please see: +// https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md +// // # Experimental // // Notice: This API is EXPERIMENTAL and may be changed or removed in a @@ -646,3 +656,23 @@ func WithResolvers(rs ...resolver.Builder) DialOption { o.resolvers = append(o.resolvers, rs...) }) } + +// WithIdleTimeout returns a DialOption that configures an idle timeout for the +// channel. If the channel is idle for the configured timeout, i.e there are no +// ongoing RPCs and no new RPCs are initiated, the channel will enter idle mode +// and as a result the name resolver and load balancer will be shut down. The +// channel will exit idle mode when the Connect() method is called or when an +// RPC is initiated. +// +// By default this feature is disabled, which can also be explicitly configured +// by passing zero to this function. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func WithIdleTimeout(d time.Duration) DialOption { + return newFuncDialOption(func(o *dialOptions) { + o.idleTimeout = d + }) +} diff --git a/vendor/google.golang.org/grpc/idle.go b/vendor/google.golang.org/grpc/idle.go new file mode 100644 index 000000000..dc3dc72f6 --- /dev/null +++ b/vendor/google.golang.org/grpc/idle.go @@ -0,0 +1,287 @@ +/* + * + * Copyright 2023 gRPC 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 grpc + +import ( + "fmt" + "math" + "sync" + "sync/atomic" + "time" +) + +// For overriding in unit tests. +var timeAfterFunc = func(d time.Duration, f func()) *time.Timer { + return time.AfterFunc(d, f) +} + +// idlenessEnforcer is the functionality provided by grpc.ClientConn to enter +// and exit from idle mode. +type idlenessEnforcer interface { + exitIdleMode() error + enterIdleMode() error +} + +// idlenessManager defines the functionality required to track RPC activity on a +// channel. +type idlenessManager interface { + onCallBegin() error + onCallEnd() + close() +} + +type noopIdlenessManager struct{} + +func (noopIdlenessManager) onCallBegin() error { return nil } +func (noopIdlenessManager) onCallEnd() {} +func (noopIdlenessManager) close() {} + +// idlenessManagerImpl implements the idlenessManager interface. It uses atomic +// operations to synchronize access to shared state and a mutex to guarantee +// mutual exclusion in a critical section. +type idlenessManagerImpl struct { + // State accessed atomically. + lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. + activeCallsCount int32 // Count of active RPCs; -math.MaxInt32 means channel is idle or is trying to get there. + activeSinceLastTimerCheck int32 // Boolean; True if there was an RPC since the last timer callback. + closed int32 // Boolean; True when the manager is closed. + + // Can be accessed without atomics or mutex since these are set at creation + // time and read-only after that. + enforcer idlenessEnforcer // Functionality provided by grpc.ClientConn. + timeout int64 // Idle timeout duration nanos stored as an int64. + + // idleMu is used to guarantee mutual exclusion in two scenarios: + // - Opposing intentions: + // - a: Idle timeout has fired and handleIdleTimeout() is trying to put + // the channel in idle mode because the channel has been inactive. + // - b: At the same time an RPC is made on the channel, and onCallBegin() + // is trying to prevent the channel from going idle. + // - Competing intentions: + // - The channel is in idle mode and there are multiple RPCs starting at + // the same time, all trying to move the channel out of idle. Only one + // of them should succeed in doing so, while the other RPCs should + // piggyback on the first one and be successfully handled. + idleMu sync.RWMutex + actuallyIdle bool + timer *time.Timer +} + +// newIdlenessManager creates a new idleness manager implementation for the +// given idle timeout. +func newIdlenessManager(enforcer idlenessEnforcer, idleTimeout time.Duration) idlenessManager { + if idleTimeout == 0 { + return noopIdlenessManager{} + } + + i := &idlenessManagerImpl{ + enforcer: enforcer, + timeout: int64(idleTimeout), + } + i.timer = timeAfterFunc(idleTimeout, i.handleIdleTimeout) + return i +} + +// resetIdleTimer resets the idle timer to the given duration. This method +// should only be called from the timer callback. +func (i *idlenessManagerImpl) resetIdleTimer(d time.Duration) { + i.idleMu.Lock() + defer i.idleMu.Unlock() + + if i.timer == nil { + // Only close sets timer to nil. We are done. + return + } + + // It is safe to ignore the return value from Reset() because this method is + // only ever called from the timer callback, which means the timer has + // already fired. + i.timer.Reset(d) +} + +// handleIdleTimeout is the timer callback that is invoked upon expiry of the +// configured idle timeout. The channel is considered inactive if there are no +// ongoing calls and no RPC activity since the last time the timer fired. +func (i *idlenessManagerImpl) handleIdleTimeout() { + if i.isClosed() { + return + } + + if atomic.LoadInt32(&i.activeCallsCount) > 0 { + i.resetIdleTimer(time.Duration(i.timeout)) + return + } + + // There has been activity on the channel since we last got here. Reset the + // timer and return. + if atomic.LoadInt32(&i.activeSinceLastTimerCheck) == 1 { + // Set the timer to fire after a duration of idle timeout, calculated + // from the time the most recent RPC completed. + atomic.StoreInt32(&i.activeSinceLastTimerCheck, 0) + i.resetIdleTimer(time.Duration(atomic.LoadInt64(&i.lastCallEndTime) + i.timeout - time.Now().UnixNano())) + return + } + + // This CAS operation is extremely likely to succeed given that there has + // been no activity since the last time we were here. Setting the + // activeCallsCount to -math.MaxInt32 indicates to onCallBegin() that the + // channel is either in idle mode or is trying to get there. + if !atomic.CompareAndSwapInt32(&i.activeCallsCount, 0, -math.MaxInt32) { + // This CAS operation can fail if an RPC started after we checked for + // activity at the top of this method, or one was ongoing from before + // the last time we were here. In both case, reset the timer and return. + i.resetIdleTimer(time.Duration(i.timeout)) + return + } + + // Now that we've set the active calls count to -math.MaxInt32, it's time to + // actually move to idle mode. + if i.tryEnterIdleMode() { + // Successfully entered idle mode. No timer needed until we exit idle. + return + } + + // Failed to enter idle mode due to a concurrent RPC that kept the channel + // active, or because of an error from the channel. Undo the attempt to + // enter idle, and reset the timer to try again later. + atomic.AddInt32(&i.activeCallsCount, math.MaxInt32) + i.resetIdleTimer(time.Duration(i.timeout)) +} + +// tryEnterIdleMode instructs the channel to enter idle mode. But before +// that, it performs a last minute check to ensure that no new RPC has come in, +// making the channel active. +// +// Return value indicates whether or not the channel moved to idle mode. +// +// Holds idleMu which ensures mutual exclusion with exitIdleMode. +func (i *idlenessManagerImpl) tryEnterIdleMode() bool { + i.idleMu.Lock() + defer i.idleMu.Unlock() + + if atomic.LoadInt32(&i.activeCallsCount) != -math.MaxInt32 { + // We raced and lost to a new RPC. Very rare, but stop entering idle. + return false + } + if atomic.LoadInt32(&i.activeSinceLastTimerCheck) == 1 { + // An very short RPC could have come in (and also finished) after we + // checked for calls count and activity in handleIdleTimeout(), but + // before the CAS operation. So, we need to check for activity again. + return false + } + + // No new RPCs have come in since we last set the active calls count value + // -math.MaxInt32 in the timer callback. And since we have the lock, it is + // safe to enter idle mode now. + if err := i.enforcer.enterIdleMode(); err != nil { + logger.Errorf("Failed to enter idle mode: %v", err) + return false + } + + // Successfully entered idle mode. + i.actuallyIdle = true + return true +} + +// onCallBegin is invoked at the start of every RPC. +func (i *idlenessManagerImpl) onCallBegin() error { + if i.isClosed() { + return nil + } + + if atomic.AddInt32(&i.activeCallsCount, 1) > 0 { + // Channel is not idle now. Set the activity bit and allow the call. + atomic.StoreInt32(&i.activeSinceLastTimerCheck, 1) + return nil + } + + // Channel is either in idle mode or is in the process of moving to idle + // mode. Attempt to exit idle mode to allow this RPC. + if err := i.exitIdleMode(); err != nil { + // Undo the increment to calls count, and return an error causing the + // RPC to fail. + atomic.AddInt32(&i.activeCallsCount, -1) + return err + } + + atomic.StoreInt32(&i.activeSinceLastTimerCheck, 1) + return nil +} + +// exitIdleMode instructs the channel to exit idle mode. +// +// Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. +func (i *idlenessManagerImpl) exitIdleMode() error { + i.idleMu.Lock() + defer i.idleMu.Unlock() + + if !i.actuallyIdle { + // This can happen in two scenarios: + // - handleIdleTimeout() set the calls count to -math.MaxInt32 and called + // tryEnterIdleMode(). But before the latter could grab the lock, an RPC + // came in and onCallBegin() noticed that the calls count is negative. + // - Channel is in idle mode, and multiple new RPCs come in at the same + // time, all of them notice a negative calls count in onCallBegin and get + // here. The first one to get the lock would got the channel to exit idle. + // + // Either way, nothing to do here. + return nil + } + + if err := i.enforcer.exitIdleMode(); err != nil { + return fmt.Errorf("channel failed to exit idle mode: %v", err) + } + + // Undo the idle entry process. This also respects any new RPC attempts. + atomic.AddInt32(&i.activeCallsCount, math.MaxInt32) + i.actuallyIdle = false + + // Start a new timer to fire after the configured idle timeout. + i.timer = timeAfterFunc(time.Duration(i.timeout), i.handleIdleTimeout) + return nil +} + +// onCallEnd is invoked at the end of every RPC. +func (i *idlenessManagerImpl) onCallEnd() { + if i.isClosed() { + return + } + + // Record the time at which the most recent call finished. + atomic.StoreInt64(&i.lastCallEndTime, time.Now().UnixNano()) + + // Decrement the active calls count. This count can temporarily go negative + // when the timer callback is in the process of moving the channel to idle + // mode, but one or more RPCs come in and complete before the timer callback + // can get done with the process of moving to idle mode. + atomic.AddInt32(&i.activeCallsCount, -1) +} + +func (i *idlenessManagerImpl) isClosed() bool { + return atomic.LoadInt32(&i.closed) == 1 +} + +func (i *idlenessManagerImpl) close() { + atomic.StoreInt32(&i.closed, 1) + + i.idleMu.Lock() + i.timer.Stop() + i.timer = nil + i.idleMu.Unlock() +} diff --git a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go index af03a40d9..755fdebc1 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go @@ -32,6 +32,9 @@ var grpclogLogger = grpclog.Component("binarylog") // Logger specifies MethodLoggers for method names with a Log call that // takes a context. +// +// This is used in the 1.0 release of gcp/observability, and thus must not be +// deleted or changed. type Logger interface { GetMethodLogger(methodName string) MethodLogger } diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go index 56fcf008d..6c3f63221 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go @@ -49,6 +49,9 @@ func (g *callIDGenerator) reset() { var idGen callIDGenerator // MethodLogger is the sub-logger for each method. +// +// This is used in the 1.0 release of gcp/observability, and thus must not be +// deleted or changed. type MethodLogger interface { Log(context.Context, LogEntryConfig) } @@ -65,6 +68,9 @@ type TruncatingMethodLogger struct { } // NewTruncatingMethodLogger returns a new truncating method logger. +// +// This is used in the 1.0 release of gcp/observability, and thus must not be +// deleted or changed. func NewTruncatingMethodLogger(h, m uint64) *TruncatingMethodLogger { return &TruncatingMethodLogger{ headerMaxLen: h, @@ -145,6 +151,9 @@ func (ml *TruncatingMethodLogger) truncateMessage(msgPb *binlogpb.Message) (trun } // LogEntryConfig represents the configuration for binary log entry. +// +// This is used in the 1.0 release of gcp/observability, and thus must not be +// deleted or changed. type LogEntryConfig interface { toProto() *binlogpb.GrpcLogEntry } diff --git a/vendor/google.golang.org/grpc/internal/buffer/unbounded.go b/vendor/google.golang.org/grpc/internal/buffer/unbounded.go index 9f6a0c120..81c2f5fd7 100644 --- a/vendor/google.golang.org/grpc/internal/buffer/unbounded.go +++ b/vendor/google.golang.org/grpc/internal/buffer/unbounded.go @@ -35,6 +35,7 @@ import "sync" // internal/transport/transport.go for an example of this. type Unbounded struct { c chan interface{} + closed bool mu sync.Mutex backlog []interface{} } @@ -47,16 +48,18 @@ func NewUnbounded() *Unbounded { // Put adds t to the unbounded buffer. func (b *Unbounded) Put(t interface{}) { b.mu.Lock() + defer b.mu.Unlock() + if b.closed { + return + } if len(b.backlog) == 0 { select { case b.c <- t: - b.mu.Unlock() return default: } } b.backlog = append(b.backlog, t) - b.mu.Unlock() } // Load sends the earliest buffered data, if any, onto the read channel @@ -64,6 +67,10 @@ func (b *Unbounded) Put(t interface{}) { // value from the read channel. func (b *Unbounded) Load() { b.mu.Lock() + defer b.mu.Unlock() + if b.closed { + return + } if len(b.backlog) > 0 { select { case b.c <- b.backlog[0]: @@ -72,7 +79,6 @@ func (b *Unbounded) Load() { default: } } - b.mu.Unlock() } // Get returns a read channel on which values added to the buffer, via Put(), @@ -80,6 +86,20 @@ func (b *Unbounded) Load() { // // Upon reading a value from this channel, users are expected to call Load() to // send the next buffered value onto the channel if there is any. +// +// If the unbounded buffer is closed, the read channel returned by this method +// is closed. func (b *Unbounded) Get() <-chan interface{} { return b.c } + +// Close closes the unbounded buffer. +func (b *Unbounded) Close() { + b.mu.Lock() + defer b.mu.Unlock() + if b.closed { + return + } + b.closed = true + close(b.c) +} diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 5ba9d94d4..80fd5c7d2 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -36,6 +36,10 @@ var ( // "GRPC_RING_HASH_CAP". This does not override the default bounds // checking which NACKs configs specifying ring sizes > 8*1024*1024 (~8M). RingHashCap = uint64FromEnv("GRPC_RING_HASH_CAP", 4096, 1, 8*1024*1024) + // PickFirstLBConfig is set if we should support configuration of the + // pick_first LB policy, which can be enabled by setting the environment + // variable "GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG" to "true". + PickFirstLBConfig = boolFromEnv("GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG", false) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/vendor/google.golang.org/grpc/internal/envconfig/observability.go b/vendor/google.golang.org/grpc/internal/envconfig/observability.go index 821dd0a7c..dd314cfb1 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/observability.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/observability.go @@ -28,9 +28,15 @@ const ( var ( // ObservabilityConfig is the json configuration for the gcp/observability // package specified directly in the envObservabilityConfig env var. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. ObservabilityConfig = os.Getenv(envObservabilityConfig) // ObservabilityConfigFile is the json configuration for the // gcp/observability specified in a file with the location specified in // envObservabilityConfigFile env var. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. ObservabilityConfigFile = os.Getenv(envObservabilityConfigFile) ) diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index 04136882c..02b4b6a1c 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -61,11 +61,10 @@ var ( // have a brand new API on the server-side and users explicitly need to use // the new API to get security integration on the server. XDSClientSideSecurity = boolFromEnv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", true) - // XDSAggregateAndDNS indicates whether processing of aggregated cluster - // and DNS cluster is enabled, which can be enabled by setting the - // environment variable - // "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" to - // "true". + // XDSAggregateAndDNS indicates whether processing of aggregated cluster and + // DNS cluster is enabled, which can be disabled by setting the environment + // variable "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" + // to "false". XDSAggregateAndDNS = boolFromEnv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER", true) // XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled, @@ -79,14 +78,18 @@ var ( // XDSFederation indicates whether federation support is enabled, which can // be enabled by setting the environment variable // "GRPC_EXPERIMENTAL_XDS_FEDERATION" to "true". - XDSFederation = boolFromEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", false) + XDSFederation = boolFromEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", true) // XDSRLS indicates whether processing of Cluster Specifier plugins and - // support for the RLS CLuster Specifier is enabled, which can be enabled by + // support for the RLS CLuster Specifier is enabled, which can be disabled by // setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to - // "true". - XDSRLS = boolFromEnv("GRPC_EXPERIMENTAL_XDS_RLS_LB", false) + // "false". + XDSRLS = boolFromEnv("GRPC_EXPERIMENTAL_XDS_RLS_LB", true) // C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing. C2PResolverTestOnlyTrafficDirectorURI = os.Getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI") + // XDSCustomLBPolicy indicates whether Custom LB Policies are enabled, which + // can be disabled by setting the environment variable + // "GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG" to "false". + XDSCustomLBPolicy = boolFromEnv("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG", true) ) diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go index 517ea7064..d08e3e907 100644 --- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go +++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go @@ -72,3 +72,17 @@ func Uint64() uint64 { defer mu.Unlock() return r.Uint64() } + +// Uint32 implements rand.Uint32 on the grpcrand global source. +func Uint32() uint32 { + mu.Lock() + defer mu.Unlock() + return r.Uint32() +} + +// Shuffle implements rand.Shuffle on the grpcrand global source. +var Shuffle = func(n int, f func(int, int)) { + mu.Lock() + defer mu.Unlock() + r.Shuffle(n, f) +} diff --git a/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go new file mode 100644 index 000000000..37b8d4117 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go @@ -0,0 +1,119 @@ +/* + * + * Copyright 2022 gRPC 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 grpcsync + +import ( + "context" + "sync" + + "google.golang.org/grpc/internal/buffer" +) + +// CallbackSerializer provides a mechanism to schedule callbacks in a +// synchronized manner. It provides a FIFO guarantee on the order of execution +// of scheduled callbacks. New callbacks can be scheduled by invoking the +// Schedule() method. +// +// This type is safe for concurrent access. +type CallbackSerializer struct { + // Done is closed once the serializer is shut down completely, i.e all + // scheduled callbacks are executed and the serializer has deallocated all + // its resources. + Done chan struct{} + + callbacks *buffer.Unbounded + closedMu sync.Mutex + closed bool +} + +// NewCallbackSerializer returns a new CallbackSerializer instance. The provided +// context will be passed to the scheduled callbacks. Users should cancel the +// provided context to shutdown the CallbackSerializer. It is guaranteed that no +// callbacks will be added once this context is canceled, and any pending un-run +// callbacks will be executed before the serializer is shut down. +func NewCallbackSerializer(ctx context.Context) *CallbackSerializer { + t := &CallbackSerializer{ + Done: make(chan struct{}), + callbacks: buffer.NewUnbounded(), + } + go t.run(ctx) + return t +} + +// Schedule adds a callback to be scheduled after existing callbacks are run. +// +// Callbacks are expected to honor the context when performing any blocking +// operations, and should return early when the context is canceled. +// +// Return value indicates if the callback was successfully added to the list of +// callbacks to be executed by the serializer. It is not possible to add +// callbacks once the context passed to NewCallbackSerializer is cancelled. +func (t *CallbackSerializer) Schedule(f func(ctx context.Context)) bool { + t.closedMu.Lock() + defer t.closedMu.Unlock() + + if t.closed { + return false + } + t.callbacks.Put(f) + return true +} + +func (t *CallbackSerializer) run(ctx context.Context) { + var backlog []func(context.Context) + + defer close(t.Done) + for ctx.Err() == nil { + select { + case <-ctx.Done(): + // Do nothing here. Next iteration of the for loop will not happen, + // since ctx.Err() would be non-nil. + case callback, ok := <-t.callbacks.Get(): + if !ok { + return + } + t.callbacks.Load() + callback.(func(ctx context.Context))(ctx) + } + } + + // Fetch pending callbacks if any, and execute them before returning from + // this method and closing t.Done. + t.closedMu.Lock() + t.closed = true + backlog = t.fetchPendingCallbacks() + t.callbacks.Close() + t.closedMu.Unlock() + for _, b := range backlog { + b(ctx) + } +} + +func (t *CallbackSerializer) fetchPendingCallbacks() []func(context.Context) { + var backlog []func(context.Context) + for { + select { + case b := <-t.callbacks.Get(): + backlog = append(backlog, b.(func(context.Context))) + t.callbacks.Load() + default: + return backlog + } + } +} diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 836b6a3b3..42ff39c84 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -60,6 +60,9 @@ var ( GetServerCredentials interface{} // func (*grpc.Server) credentials.TransportCredentials // CanonicalString returns the canonical string of the code defined here: // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. CanonicalString interface{} // func (codes.Code) string // DrainServerTransports initiates a graceful close of existing connections // on a gRPC server accepted on the provided listener address. An @@ -69,20 +72,35 @@ var ( // AddGlobalServerOptions adds an array of ServerOption that will be // effective globally for newly created servers. The priority will be: 1. // user-provided; 2. this method; 3. default values. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. AddGlobalServerOptions interface{} // func(opt ...ServerOption) // ClearGlobalServerOptions clears the array of extra ServerOption. This // method is useful in testing and benchmarking. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. ClearGlobalServerOptions func() // AddGlobalDialOptions adds an array of DialOption that will be effective // globally for newly created client channels. The priority will be: 1. // user-provided; 2. this method; 3. default values. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. AddGlobalDialOptions interface{} // func(opt ...DialOption) // DisableGlobalDialOptions returns a DialOption that prevents the // ClientConn from applying the global DialOptions (set via // AddGlobalDialOptions). + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. DisableGlobalDialOptions interface{} // func() grpc.DialOption // ClearGlobalDialOptions clears the array of extra DialOption. This // method is useful in testing and benchmarking. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. ClearGlobalDialOptions func() // JoinDialOptions combines the dial options passed as arguments into a // single dial option. @@ -93,9 +111,15 @@ var ( // WithBinaryLogger returns a DialOption that specifies the binary logger // for a ClientConn. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. WithBinaryLogger interface{} // func(binarylog.Logger) grpc.DialOption // BinaryLogger returns a ServerOption that can set the binary logger for a // server. + // + // This is used in the 1.0 release of gcp/observability, and thus must not be + // deleted or changed. BinaryLogger interface{} // func(binarylog.Logger) grpc.ServerOption // NewXDSResolverWithConfigForTesting creates a new xds resolver builder using diff --git a/vendor/google.golang.org/grpc/internal/serviceconfig/duration.go b/vendor/google.golang.org/grpc/internal/serviceconfig/duration.go new file mode 100644 index 000000000..11d82afcc --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/serviceconfig/duration.go @@ -0,0 +1,130 @@ +/* + * + * Copyright 2023 gRPC 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 serviceconfig + +import ( + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "time" +) + +// Duration defines JSON marshal and unmarshal methods to conform to the +// protobuf JSON spec defined [here]. +// +// [here]: https://protobuf.dev/reference/protobuf/google.protobuf/#duration +type Duration time.Duration + +func (d Duration) String() string { + return fmt.Sprint(time.Duration(d)) +} + +// MarshalJSON converts from d to a JSON string output. +func (d Duration) MarshalJSON() ([]byte, error) { + ns := time.Duration(d).Nanoseconds() + sec := ns / int64(time.Second) + ns = ns % int64(time.Second) + + var sign string + if sec < 0 || ns < 0 { + sign, sec, ns = "-", -1*sec, -1*ns + } + + // Generated output always contains 0, 3, 6, or 9 fractional digits, + // depending on required precision. + str := fmt.Sprintf("%s%d.%09d", sign, sec, ns) + str = strings.TrimSuffix(str, "000") + str = strings.TrimSuffix(str, "000") + str = strings.TrimSuffix(str, ".000") + return []byte(fmt.Sprintf("\"%ss\"", str)), nil +} + +// UnmarshalJSON unmarshals b as a duration JSON string into d. +func (d *Duration) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + if !strings.HasSuffix(s, "s") { + return fmt.Errorf("malformed duration %q: missing seconds unit", s) + } + neg := false + if s[0] == '-' { + neg = true + s = s[1:] + } + ss := strings.SplitN(s[:len(s)-1], ".", 3) + if len(ss) > 2 { + return fmt.Errorf("malformed duration %q: too many decimals", s) + } + // hasDigits is set if either the whole or fractional part of the number is + // present, since both are optional but one is required. + hasDigits := false + var sec, ns int64 + if len(ss[0]) > 0 { + var err error + if sec, err = strconv.ParseInt(ss[0], 10, 64); err != nil { + return fmt.Errorf("malformed duration %q: %v", s, err) + } + // Maximum seconds value per the durationpb spec. + const maxProtoSeconds = 315_576_000_000 + if sec > maxProtoSeconds { + return fmt.Errorf("out of range: %q", s) + } + hasDigits = true + } + if len(ss) == 2 && len(ss[1]) > 0 { + if len(ss[1]) > 9 { + return fmt.Errorf("malformed duration %q: too many digits after decimal", s) + } + var err error + if ns, err = strconv.ParseInt(ss[1], 10, 64); err != nil { + return fmt.Errorf("malformed duration %q: %v", s, err) + } + for i := 9; i > len(ss[1]); i-- { + ns *= 10 + } + hasDigits = true + } + if !hasDigits { + return fmt.Errorf("malformed duration %q: contains no numbers", s) + } + + if neg { + sec *= -1 + ns *= -1 + } + + // Maximum/minimum seconds/nanoseconds representable by Go's time.Duration. + const maxSeconds = math.MaxInt64 / int64(time.Second) + const maxNanosAtMaxSeconds = math.MaxInt64 % int64(time.Second) + const minSeconds = math.MinInt64 / int64(time.Second) + const minNanosAtMinSeconds = math.MinInt64 % int64(time.Second) + + if sec > maxSeconds || (sec == maxSeconds && ns >= maxNanosAtMaxSeconds) { + *d = Duration(math.MaxInt64) + } else if sec < minSeconds || (sec == minSeconds && ns <= minNanosAtMinSeconds) { + *d = Duration(math.MinInt64) + } else { + *d = Duration(sec*int64(time.Second) + ns) + } + return nil +} diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index c343c23a5..be5a9c81e 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -30,6 +30,7 @@ import ( "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" + "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/status" ) @@ -488,12 +489,13 @@ type loopyWriter struct { bdpEst *bdpEstimator draining bool conn net.Conn + logger *grpclog.PrefixLogger // Side-specific handlers ssGoAwayHandler func(*goAway) (bool, error) } -func newLoopyWriter(s side, fr *framer, cbuf *controlBuffer, bdpEst *bdpEstimator, conn net.Conn) *loopyWriter { +func newLoopyWriter(s side, fr *framer, cbuf *controlBuffer, bdpEst *bdpEstimator, conn net.Conn, logger *grpclog.PrefixLogger) *loopyWriter { var buf bytes.Buffer l := &loopyWriter{ side: s, @@ -507,6 +509,7 @@ func newLoopyWriter(s side, fr *framer, cbuf *controlBuffer, bdpEst *bdpEstimato hEnc: hpack.NewEncoder(&buf), bdpEst: bdpEst, conn: conn, + logger: logger, } return l } @@ -536,8 +539,8 @@ const minBatchSize = 1000 // left open to allow the I/O error to be encountered by the reader instead. func (l *loopyWriter) run() (err error) { defer func() { - if logger.V(logLevel) { - logger.Infof("transport: loopyWriter exiting with error: %v", err) + if l.logger.V(logLevel) { + l.logger.Infof("loopyWriter exiting with error: %v", err) } if !isIOError(err) { l.framer.writer.Flush() @@ -636,8 +639,8 @@ func (l *loopyWriter) headerHandler(h *headerFrame) error { if l.side == serverSide { str, ok := l.estdStreams[h.streamID] if !ok { - if logger.V(logLevel) { - logger.Warningf("transport: loopy doesn't recognize the stream: %d", h.streamID) + if l.logger.V(logLevel) { + l.logger.Infof("Unrecognized streamID %d in loopyWriter", h.streamID) } return nil } @@ -692,8 +695,8 @@ func (l *loopyWriter) writeHeader(streamID uint32, endStream bool, hf []hpack.He l.hBuf.Reset() for _, f := range hf { if err := l.hEnc.WriteField(f); err != nil { - if logger.V(logLevel) { - logger.Warningf("transport: loopyWriter.writeHeader encountered error while encoding headers: %v", err) + if l.logger.V(logLevel) { + l.logger.Warningf("Encountered error while encoding headers: %v", err) } } } diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go index e6626bf96..98f80e3fa 100644 --- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go @@ -39,6 +39,7 @@ import ( "golang.org/x/net/http2" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" @@ -83,6 +84,7 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []s contentSubtype: contentSubtype, stats: stats, } + st.logger = prefixLoggerForServerHandlerTransport(st) if v := r.Header.Get("grpc-timeout"); v != "" { to, err := decodeTimeout(v) @@ -150,13 +152,14 @@ type serverHandlerTransport struct { // TODO make sure this is consistent across handler_server and http2_server contentSubtype string - stats []stats.Handler + stats []stats.Handler + logger *grpclog.PrefixLogger } func (ht *serverHandlerTransport) Close(err error) { ht.closeOnce.Do(func() { - if logger.V(logLevel) { - logger.Infof("Closing serverHandlerTransport: %v", err) + if ht.logger.V(logLevel) { + ht.logger.Infof("Closing: %v", err) } close(ht.closedCh) }) @@ -450,7 +453,7 @@ func (ht *serverHandlerTransport) IncrMsgSent() {} func (ht *serverHandlerTransport) IncrMsgRecv() {} -func (ht *serverHandlerTransport) Drain() { +func (ht *serverHandlerTransport) Drain(debugData string) { panic("Drain() is not implemented") } diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index 9826feb8c..326bf0848 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -38,6 +38,7 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal/channelz" icredentials "google.golang.org/grpc/internal/credentials" + "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" imetadata "google.golang.org/grpc/internal/metadata" @@ -145,6 +146,7 @@ type http2Client struct { bufferPool *bufferPool connectionID uint64 + logger *grpclog.PrefixLogger } func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr resolver.Address, useProxy bool, grpcUA string) (net.Conn, error) { @@ -244,7 +246,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts if err := connectCtx.Err(); err != nil { // connectCtx expired before exiting the function. Hard close the connection. if logger.V(logLevel) { - logger.Infof("newClientTransport: aborting due to connectCtx: %v", err) + logger.Infof("Aborting due to connect deadline expiring: %v", err) } conn.Close() } @@ -346,6 +348,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts bufferPool: newBufferPool(), onClose: onClose, } + t.logger = prefixLoggerForClientTransport(t) // Add peer information to the http2client context. t.ctx = peer.NewContext(t.ctx, t.getPeer()) @@ -444,7 +447,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts return nil, err } go func() { - t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst, t.conn) + t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) t.loopy.run() close(t.writerDone) }() @@ -782,7 +785,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, s.id = h.streamID s.fc = &inFlow{limit: uint32(t.initialWindowSize)} t.mu.Lock() - if t.activeStreams == nil { // Can be niled from Close(). + if t.state == draining || t.activeStreams == nil { // Can be niled from Close(). t.mu.Unlock() return false // Don't create a stream if the transport is already closed. } @@ -859,8 +862,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, } } if transportDrainRequired { - if logger.V(logLevel) { - logger.Infof("transport: t.nextID > MaxStreamID. Draining") + if t.logger.V(logLevel) { + t.logger.Infof("Draining transport: t.nextID > MaxStreamID") } t.GracefulClose() } @@ -952,8 +955,8 @@ func (t *http2Client) Close(err error) { t.mu.Unlock() return } - if logger.V(logLevel) { - logger.Infof("transport: closing: %v", err) + if t.logger.V(logLevel) { + t.logger.Infof("Closing: %v", err) } // Call t.onClose ASAP to prevent the client from attempting to create new // streams. @@ -1009,8 +1012,8 @@ func (t *http2Client) GracefulClose() { t.mu.Unlock() return } - if logger.V(logLevel) { - logger.Infof("transport: GracefulClose called") + if t.logger.V(logLevel) { + t.logger.Infof("GracefulClose called") } t.onClose(GoAwayInvalid) t.state = draining @@ -1174,8 +1177,8 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) { } statusCode, ok := http2ErrConvTab[f.ErrCode] if !ok { - if logger.V(logLevel) { - logger.Warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error: %v", f.ErrCode) + if t.logger.V(logLevel) { + t.logger.Infof("Received a RST_STREAM frame with code %q, but found no mapped gRPC status", f.ErrCode) } statusCode = codes.Unknown } @@ -1334,7 +1337,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) { // setGoAwayReason sets the value of t.goAwayReason based // on the GoAway frame received. -// It expects a lock on transport's mutext to be held by +// It expects a lock on transport's mutex to be held by // the caller. func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) { t.goAwayReason = GoAwayNoReason diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 99ae1a737..ec4eef213 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -35,7 +35,9 @@ import ( "github.com/golang/protobuf/proto" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" + "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpcutil" + "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/syscall" "google.golang.org/grpc/codes" @@ -129,6 +131,8 @@ type http2Server struct { // This lock may not be taken if mu is already held. maxStreamMu sync.Mutex maxStreamID uint32 // max stream ID ever seen + + logger *grpclog.PrefixLogger } // NewServerTransport creates a http2 transport with conn and configuration @@ -167,15 +171,10 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, ID: http2.SettingMaxFrameSize, Val: http2MaxFrameLen, }} - // TODO(zhaoq): Have a better way to signal "no limit" because 0 is - // permitted in the HTTP2 spec. - maxStreams := config.MaxStreams - if maxStreams == 0 { - maxStreams = math.MaxUint32 - } else { + if config.MaxStreams != math.MaxUint32 { isettings = append(isettings, http2.Setting{ ID: http2.SettingMaxConcurrentStreams, - Val: maxStreams, + Val: config.MaxStreams, }) } dynamicWindow := true @@ -254,7 +253,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, framer: framer, readerDone: make(chan struct{}), writerDone: make(chan struct{}), - maxStreams: maxStreams, + maxStreams: config.MaxStreams, inTapHandle: config.InTapHandle, fc: &trInFlow{limit: uint32(icwz)}, state: reachable, @@ -267,6 +266,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, czData: new(channelzData), bufferPool: newBufferPool(), } + t.logger = prefixLoggerForServerTransport(t) // Add peer information to the http2server context. t.ctx = peer.NewContext(t.ctx, t.getPeer()) @@ -331,7 +331,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, t.handleSettings(sf) go func() { - t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn) + t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler t.loopy.run() close(t.writerDone) @@ -425,8 +425,8 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( // "Transports must consider requests containing the Connection header // as malformed." - A41 case "connection": - if logger.V(logLevel) { - logger.Errorf("transport: http2Server.operateHeaders parsed a :connection header which makes a request malformed as per the HTTP/2 spec") + if t.logger.V(logLevel) { + t.logger.Infof("Received a HEADERS frame with a :connection header which makes the request malformed, as per the HTTP/2 spec") } protocolError = true default: @@ -436,7 +436,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( v, err := decodeMetadataHeader(hf.Name, hf.Value) if err != nil { headerError = status.Newf(codes.Internal, "malformed binary metadata %q in header %q: %v", hf.Value, hf.Name, err) - logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err) + t.logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err) break } mdata[hf.Name] = append(mdata[hf.Name], v) @@ -450,8 +450,8 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( // error, this takes precedence over a client not speaking gRPC. if len(mdata[":authority"]) > 1 || len(mdata["host"]) > 1 { errMsg := fmt.Sprintf("num values of :authority: %v, num values of host: %v, both must only have 1 value as per HTTP/2 spec", len(mdata[":authority"]), len(mdata["host"])) - if logger.V(logLevel) { - logger.Errorf("transport: %v", errMsg) + if t.logger.V(logLevel) { + t.logger.Infof("Aborting the stream early: %v", errMsg) } t.controlBuf.put(&earlyAbortStream{ httpStatus: http.StatusBadRequest, @@ -545,9 +545,9 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( } if httpMethod != http.MethodPost { t.mu.Unlock() - errMsg := fmt.Sprintf("http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) - if logger.V(logLevel) { - logger.Infof("transport: %v", errMsg) + errMsg := fmt.Sprintf("Received a HEADERS frame with :method %q which should be POST", httpMethod) + if t.logger.V(logLevel) { + t.logger.Infof("Aborting the stream early: %v", errMsg) } t.controlBuf.put(&earlyAbortStream{ httpStatus: 405, @@ -563,8 +563,8 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( var err error if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method}); err != nil { t.mu.Unlock() - if logger.V(logLevel) { - logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err) + if t.logger.V(logLevel) { + t.logger.Infof("Aborting the stream early due to InTapHandle failure: %v", err) } stat, ok := status.FromError(err) if !ok { @@ -638,8 +638,8 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context. atomic.StoreInt64(&t.lastRead, time.Now().UnixNano()) if err != nil { if se, ok := err.(http2.StreamError); ok { - if logger.V(logLevel) { - logger.Warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se) + if t.logger.V(logLevel) { + t.logger.Warningf("Encountered http2.StreamError: %v", se) } t.mu.Lock() s := t.activeStreams[se.StreamID] @@ -682,8 +682,8 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context. case *http2.GoAwayFrame: // TODO: Handle GoAway from the client appropriately. default: - if logger.V(logLevel) { - logger.Errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame) + if t.logger.V(logLevel) { + t.logger.Infof("Received unsupported frame type %T", frame) } } } @@ -942,8 +942,8 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool { var sz int64 for _, f := range hdrFrame.hf { if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) { - if logger.V(logLevel) { - logger.Errorf("header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize) + if t.logger.V(logLevel) { + t.logger.Infof("Header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize) } return false } @@ -1056,7 +1056,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error { stBytes, err := proto.Marshal(p) if err != nil { // TODO: return error instead, when callers are able to handle it. - logger.Errorf("transport: failed to marshal rpc status: %v, error: %v", p, err) + t.logger.Errorf("Failed to marshal rpc status: %s, error: %v", pretty.ToJSON(p), err) } else { headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)}) } @@ -1161,18 +1161,18 @@ func (t *http2Server) keepalive() { if val <= 0 { // The connection has been idle for a duration of keepalive.MaxConnectionIdle or more. // Gracefully close the connection. - t.Drain() + t.Drain("max_idle") return } idleTimer.Reset(val) case <-ageTimer.C: - t.Drain() + t.Drain("max_age") ageTimer.Reset(t.kp.MaxConnectionAgeGrace) select { case <-ageTimer.C: // Close the connection after grace period. - if logger.V(logLevel) { - logger.Infof("transport: closing server transport due to maximum connection age.") + if t.logger.V(logLevel) { + t.logger.Infof("Closing server transport due to maximum connection age") } t.controlBuf.put(closeConnection{}) case <-t.done: @@ -1223,8 +1223,8 @@ func (t *http2Server) Close(err error) { t.mu.Unlock() return } - if logger.V(logLevel) { - logger.Infof("transport: closing: %v", err) + if t.logger.V(logLevel) { + t.logger.Infof("Closing: %v", err) } t.state = closing streams := t.activeStreams @@ -1232,8 +1232,8 @@ func (t *http2Server) Close(err error) { t.mu.Unlock() t.controlBuf.finish() close(t.done) - if err := t.conn.Close(); err != nil && logger.V(logLevel) { - logger.Infof("transport: error closing conn during Close: %v", err) + if err := t.conn.Close(); err != nil && t.logger.V(logLevel) { + t.logger.Infof("Error closing underlying net.Conn during Close: %v", err) } channelz.RemoveEntry(t.channelzID) // Cancel all active streams. @@ -1313,14 +1313,14 @@ func (t *http2Server) RemoteAddr() net.Addr { return t.remoteAddr } -func (t *http2Server) Drain() { +func (t *http2Server) Drain(debugData string) { t.mu.Lock() defer t.mu.Unlock() if t.drainEvent != nil { return } t.drainEvent = grpcsync.NewEvent() - t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte{}, headsUp: true}) + t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte(debugData), headsUp: true}) } var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}} @@ -1362,7 +1362,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { // originated before the GoAway reaches the client. // After getting the ack or timer expiration send out another GoAway this // time with an ID of the max stream server intends to process. - if err := t.framer.fr.WriteGoAway(math.MaxUint32, http2.ErrCodeNo, []byte{}); err != nil { + if err := t.framer.fr.WriteGoAway(math.MaxUint32, http2.ErrCodeNo, g.debugData); err != nil { return false, err } if err := t.framer.fr.WritePing(false, goAwayPing.data); err != nil { diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go index 8fcae4f4d..19cbb18f5 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http_util.go +++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go @@ -38,7 +38,6 @@ import ( "golang.org/x/net/http2/hpack" spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" "google.golang.org/grpc/status" ) @@ -86,7 +85,6 @@ var ( // 504 Gateway timeout - UNAVAILABLE. http.StatusGatewayTimeout: codes.Unavailable, } - logger = grpclog.Component("transport") ) // isReservedHeader checks whether hdr belongs to HTTP2 headers diff --git a/vendor/google.golang.org/grpc/internal/transport/logging.go b/vendor/google.golang.org/grpc/internal/transport/logging.go new file mode 100644 index 000000000..42ed2b07a --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/transport/logging.go @@ -0,0 +1,40 @@ +/* + * + * Copyright 2023 gRPC 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 transport + +import ( + "fmt" + + "google.golang.org/grpc/grpclog" + internalgrpclog "google.golang.org/grpc/internal/grpclog" +) + +var logger = grpclog.Component("transport") + +func prefixLoggerForServerTransport(p *http2Server) *internalgrpclog.PrefixLogger { + return internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[server-transport %p] ", p)) +} + +func prefixLoggerForServerHandlerTransport(p *serverHandlerTransport) *internalgrpclog.PrefixLogger { + return internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[server-handler-transport %p] ", p)) +} + +func prefixLoggerForClientTransport(p *http2Client) *internalgrpclog.PrefixLogger { + return internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[client-transport %p] ", p)) +} diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 1b7d7fabc..aa1c89659 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -726,7 +726,7 @@ type ServerTransport interface { RemoteAddr() net.Addr // Drain notifies the client this ServerTransport stops accepting new RPCs. - Drain() + Drain(debugData string) // IncrMsgSent increments the number of message sent through this transport. IncrMsgSent() diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go index c525dc070..02f975951 100644 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ b/vendor/google.golang.org/grpc/picker_wrapper.go @@ -36,6 +36,7 @@ import ( type pickerWrapper struct { mu sync.Mutex done bool + idle bool blockingCh chan struct{} picker balancer.Picker } @@ -47,7 +48,11 @@ func newPickerWrapper() *pickerWrapper { // updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. func (pw *pickerWrapper) updatePicker(p balancer.Picker) { pw.mu.Lock() - if pw.done { + if pw.done || pw.idle { + // There is a small window where a picker update from the LB policy can + // race with the channel going to idle mode. If the picker is idle here, + // it is because the channel asked it to do so, and therefore it is sage + // to ignore the update from the LB policy. pw.mu.Unlock() return } @@ -63,10 +68,8 @@ func (pw *pickerWrapper) updatePicker(p balancer.Picker) { // - wraps the done function in the passed in result to increment the calls // failed or calls succeeded channelz counter before invoking the actual // done function. -func doneChannelzWrapper(acw *acBalancerWrapper, result *balancer.PickResult) { - acw.mu.Lock() - ac := acw.ac - acw.mu.Unlock() +func doneChannelzWrapper(acbw *acBalancerWrapper, result *balancer.PickResult) { + ac := acbw.ac ac.incrCallsStarted() done := result.Done result.Done = func(b balancer.DoneInfo) { @@ -152,14 +155,14 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. return nil, balancer.PickResult{}, status.Error(codes.Unavailable, err.Error()) } - acw, ok := pickResult.SubConn.(*acBalancerWrapper) + acbw, ok := pickResult.SubConn.(*acBalancerWrapper) if !ok { logger.Errorf("subconn returned from pick is type %T, not *acBalancerWrapper", pickResult.SubConn) continue } - if t := acw.getAddrConn().getReadyTransport(); t != nil { + if t := acbw.ac.getReadyTransport(); t != nil { if channelz.IsOn() { - doneChannelzWrapper(acw, &pickResult) + doneChannelzWrapper(acbw, &pickResult) return t, pickResult, nil } return t, pickResult, nil @@ -187,6 +190,25 @@ func (pw *pickerWrapper) close() { close(pw.blockingCh) } +func (pw *pickerWrapper) enterIdleMode() { + pw.mu.Lock() + defer pw.mu.Unlock() + if pw.done { + return + } + pw.idle = true +} + +func (pw *pickerWrapper) exitIdleMode() { + pw.mu.Lock() + defer pw.mu.Unlock() + if pw.done { + return + } + pw.blockingCh = make(chan struct{}) + pw.idle = false +} + // dropError is a wrapper error that indicates the LB policy wishes to drop the // RPC and not retry it. type dropError struct { diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/pickfirst.go index fc91b4d26..abe266b02 100644 --- a/vendor/google.golang.org/grpc/pickfirst.go +++ b/vendor/google.golang.org/grpc/pickfirst.go @@ -19,11 +19,15 @@ package grpc import ( + "encoding/json" "errors" "fmt" "google.golang.org/grpc/balancer" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/internal/envconfig" + "google.golang.org/grpc/internal/grpcrand" + "google.golang.org/grpc/serviceconfig" ) // PickFirstBalancerName is the name of the pick_first balancer. @@ -43,10 +47,28 @@ func (*pickfirstBuilder) Name() string { return PickFirstBalancerName } +type pfConfig struct { + serviceconfig.LoadBalancingConfig `json:"-"` + + // If set to true, instructs the LB policy to shuffle the order of the list + // of addresses received from the name resolver before attempting to + // connect to them. + ShuffleAddressList bool `json:"shuffleAddressList"` +} + +func (*pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { + cfg := &pfConfig{} + if err := json.Unmarshal(js, cfg); err != nil { + return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err) + } + return cfg, nil +} + type pickfirstBalancer struct { state connectivity.State cc balancer.ClientConn subConn balancer.SubConn + cfg *pfConfig } func (b *pickfirstBalancer) ResolverError(err error) { @@ -69,7 +91,8 @@ func (b *pickfirstBalancer) ResolverError(err error) { } func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { - if len(state.ResolverState.Addresses) == 0 { + addrs := state.ResolverState.Addresses + if len(addrs) == 0 { // The resolver reported an empty address list. Treat it like an error by // calling b.ResolverError. if b.subConn != nil { @@ -82,12 +105,23 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState return balancer.ErrBadResolverState } + if state.BalancerConfig != nil { + cfg, ok := state.BalancerConfig.(*pfConfig) + if !ok { + return fmt.Errorf("pickfirstBalancer: received nil or illegal BalancerConfig (type %T): %v", state.BalancerConfig, state.BalancerConfig) + } + b.cfg = cfg + } + + if envconfig.PickFirstLBConfig && b.cfg != nil && b.cfg.ShuffleAddressList { + grpcrand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) + } if b.subConn != nil { - b.cc.UpdateAddresses(b.subConn, state.ResolverState.Addresses) + b.cc.UpdateAddresses(b.subConn, addrs) return nil } - subConn, err := b.cc.NewSubConn(state.ResolverState.Addresses, balancer.NewSubConnOptions{}) + subConn, err := b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{}) if err != nil { if logger.V(2) { logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) @@ -119,7 +153,6 @@ func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state b } return } - b.state = state.ConnectivityState if state.ConnectivityState == connectivity.Shutdown { b.subConn = nil return @@ -132,11 +165,21 @@ func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state b Picker: &picker{result: balancer.PickResult{SubConn: subConn}}, }) case connectivity.Connecting: + if b.state == connectivity.TransientFailure { + // We stay in TransientFailure until we are Ready. See A62. + return + } b.cc.UpdateState(balancer.State{ ConnectivityState: state.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}, }) case connectivity.Idle: + if b.state == connectivity.TransientFailure { + // We stay in TransientFailure until we are Ready. Also kick the + // subConn out of Idle into Connecting. See A62. + b.subConn.Connect() + return + } b.cc.UpdateState(balancer.State{ ConnectivityState: state.ConnectivityState, Picker: &idlePicker{subConn: subConn}, @@ -147,6 +190,7 @@ func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state b Picker: &picker{err: state.ConnectionError}, }) } + b.state = state.ConnectivityState } func (b *pickfirstBalancer) Close() { diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go index 6215e5ef2..353c10b69 100644 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/vendor/google.golang.org/grpc/resolver/resolver.go @@ -22,13 +22,13 @@ package resolver import ( "context" + "fmt" "net" "net/url" "strings" "google.golang.org/grpc/attributes" "google.golang.org/grpc/credentials" - "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/serviceconfig" ) @@ -124,7 +124,7 @@ type Address struct { Attributes *attributes.Attributes // BalancerAttributes contains arbitrary data about this address intended - // for consumption by the LB policy. These attribes do not affect SubConn + // for consumption by the LB policy. These attributes do not affect SubConn // creation, connection establishment, handshaking, etc. BalancerAttributes *attributes.Attributes @@ -151,7 +151,17 @@ func (a Address) Equal(o Address) bool { // String returns JSON formatted string representation of the address. func (a Address) String() string { - return pretty.ToJSON(a) + var sb strings.Builder + sb.WriteString(fmt.Sprintf("{Addr: %q, ", a.Addr)) + sb.WriteString(fmt.Sprintf("ServerName: %q, ", a.ServerName)) + if a.Attributes != nil { + sb.WriteString(fmt.Sprintf("Attributes: %v, ", a.Attributes.String())) + } + if a.BalancerAttributes != nil { + sb.WriteString(fmt.Sprintf("BalancerAttributes: %v", a.BalancerAttributes.String())) + } + sb.WriteString("}") + return sb.String() } // BuildOptions includes additional information for the builder to create diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go index 05a9d4e0b..b408b3688 100644 --- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go +++ b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go @@ -19,11 +19,11 @@ package grpc import ( + "context" "strings" "sync" "google.golang.org/grpc/balancer" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/pretty" @@ -31,129 +31,192 @@ import ( "google.golang.org/grpc/serviceconfig" ) +// resolverStateUpdater wraps the single method used by ccResolverWrapper to +// report a state update from the actual resolver implementation. +type resolverStateUpdater interface { + updateResolverState(s resolver.State, err error) error +} + // ccResolverWrapper is a wrapper on top of cc for resolvers. // It implements resolver.ClientConn interface. type ccResolverWrapper struct { - cc *ClientConn - resolverMu sync.Mutex - resolver resolver.Resolver - done *grpcsync.Event - curState resolver.State + // The following fields are initialized when the wrapper is created and are + // read-only afterwards, and therefore can be accessed without a mutex. + cc resolverStateUpdater + channelzID *channelz.Identifier + ignoreServiceConfig bool + opts ccResolverWrapperOpts + serializer *grpcsync.CallbackSerializer // To serialize all incoming calls. + serializerCancel context.CancelFunc // To close the serializer, accessed only from close(). + + // All incoming (resolver --> gRPC) calls are guaranteed to execute in a + // mutually exclusive manner as they are scheduled on the serializer. + // Fields accessed *only* in these serializer callbacks, can therefore be + // accessed without a mutex. + curState resolver.State + + // mu guards access to the below fields. + mu sync.Mutex + closed bool + resolver resolver.Resolver // Accessed only from outgoing calls. +} - incomingMu sync.Mutex // Synchronizes all the incoming calls. +// ccResolverWrapperOpts wraps the arguments to be passed when creating a new +// ccResolverWrapper. +type ccResolverWrapperOpts struct { + target resolver.Target // User specified dial target to resolve. + builder resolver.Builder // Resolver builder to use. + bOpts resolver.BuildOptions // Resolver build options to use. + channelzID *channelz.Identifier // Channelz identifier for the channel. } // newCCResolverWrapper uses the resolver.Builder to build a Resolver and // returns a ccResolverWrapper object which wraps the newly built resolver. -func newCCResolverWrapper(cc *ClientConn, rb resolver.Builder) (*ccResolverWrapper, error) { +func newCCResolverWrapper(cc resolverStateUpdater, opts ccResolverWrapperOpts) (*ccResolverWrapper, error) { + ctx, cancel := context.WithCancel(context.Background()) ccr := &ccResolverWrapper{ - cc: cc, - done: grpcsync.NewEvent(), - } - - var credsClone credentials.TransportCredentials - if creds := cc.dopts.copts.TransportCredentials; creds != nil { - credsClone = creds.Clone() - } - rbo := resolver.BuildOptions{ - DisableServiceConfig: cc.dopts.disableServiceConfig, - DialCreds: credsClone, - CredsBundle: cc.dopts.copts.CredsBundle, - Dialer: cc.dopts.copts.Dialer, - } - - var err error - // We need to hold the lock here while we assign to the ccr.resolver field - // to guard against a data race caused by the following code path, - // rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up - // accessing ccr.resolver which is being assigned here. - ccr.resolverMu.Lock() - defer ccr.resolverMu.Unlock() - ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo) + cc: cc, + channelzID: opts.channelzID, + ignoreServiceConfig: opts.bOpts.DisableServiceConfig, + opts: opts, + serializer: grpcsync.NewCallbackSerializer(ctx), + serializerCancel: cancel, + } + + // Cannot hold the lock at build time because the resolver can send an + // update or error inline and these incoming calls grab the lock to schedule + // a callback in the serializer. + r, err := opts.builder.Build(opts.target, ccr, opts.bOpts) if err != nil { + cancel() return nil, err } + + // Any error reported by the resolver at build time that leads to a + // re-resolution request from the balancer is dropped by grpc until we + // return from this function. So, we don't have to handle pending resolveNow + // requests here. + ccr.mu.Lock() + ccr.resolver = r + ccr.mu.Unlock() + return ccr, nil } func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) { - ccr.resolverMu.Lock() - if !ccr.done.HasFired() { - ccr.resolver.ResolveNow(o) + ccr.mu.Lock() + defer ccr.mu.Unlock() + + // ccr.resolver field is set only after the call to Build() returns. But in + // the process of building, the resolver may send an error update which when + // propagated to the balancer may result in a re-resolution request. + if ccr.closed || ccr.resolver == nil { + return } - ccr.resolverMu.Unlock() + ccr.resolver.ResolveNow(o) } func (ccr *ccResolverWrapper) close() { - ccr.resolverMu.Lock() - ccr.resolver.Close() - ccr.done.Fire() - ccr.resolverMu.Unlock() + ccr.mu.Lock() + if ccr.closed { + ccr.mu.Unlock() + return + } + + channelz.Info(logger, ccr.channelzID, "Closing the name resolver") + + // Close the serializer to ensure that no more calls from the resolver are + // handled, before actually closing the resolver. + ccr.serializerCancel() + ccr.closed = true + r := ccr.resolver + ccr.mu.Unlock() + + // Give enqueued callbacks a chance to finish. + <-ccr.serializer.Done + + // Spawn a goroutine to close the resolver (since it may block trying to + // cleanup all allocated resources) and return early. + go r.Close() +} + +// serializerScheduleLocked is a convenience method to schedule a function to be +// run on the serializer while holding ccr.mu. +func (ccr *ccResolverWrapper) serializerScheduleLocked(f func(context.Context)) { + ccr.mu.Lock() + ccr.serializer.Schedule(f) + ccr.mu.Unlock() } +// UpdateState is called by resolver implementations to report new state to gRPC +// which includes addresses and service config. func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { - ccr.incomingMu.Lock() - defer ccr.incomingMu.Unlock() - if ccr.done.HasFired() { + errCh := make(chan error, 1) + ok := ccr.serializer.Schedule(func(context.Context) { + ccr.addChannelzTraceEvent(s) + ccr.curState = s + if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState { + errCh <- balancer.ErrBadResolverState + return + } + errCh <- nil + }) + if !ok { + // The only time when Schedule() fail to add the callback to the + // serializer is when the serializer is closed, and this happens only + // when the resolver wrapper is closed. return nil } - ccr.addChannelzTraceEvent(s) - ccr.curState = s - if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState { - return balancer.ErrBadResolverState - } - return nil + return <-errCh } +// ReportError is called by resolver implementations to report errors +// encountered during name resolution to gRPC. func (ccr *ccResolverWrapper) ReportError(err error) { - ccr.incomingMu.Lock() - defer ccr.incomingMu.Unlock() - if ccr.done.HasFired() { - return - } - channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err) - ccr.cc.updateResolverState(resolver.State{}, err) + ccr.serializerScheduleLocked(func(_ context.Context) { + channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: reporting error to cc: %v", err) + ccr.cc.updateResolverState(resolver.State{}, err) + }) } -// NewAddress is called by the resolver implementation to send addresses to gRPC. +// NewAddress is called by the resolver implementation to send addresses to +// gRPC. func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { - ccr.incomingMu.Lock() - defer ccr.incomingMu.Unlock() - if ccr.done.HasFired() { - return - } - ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) - ccr.curState.Addresses = addrs - ccr.cc.updateResolverState(ccr.curState, nil) + ccr.serializerScheduleLocked(func(_ context.Context) { + ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) + ccr.curState.Addresses = addrs + ccr.cc.updateResolverState(ccr.curState, nil) + }) } // NewServiceConfig is called by the resolver implementation to send service // configs to gRPC. func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { - ccr.incomingMu.Lock() - defer ccr.incomingMu.Unlock() - if ccr.done.HasFired() { - return - } - channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: got new service config: %s", sc) - if ccr.cc.dopts.disableServiceConfig { - channelz.Info(logger, ccr.cc.channelzID, "Service config lookups disabled; ignoring config") - return - } - scpr := parseServiceConfig(sc) - if scpr.Err != nil { - channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err) - return - } - ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) - ccr.curState.ServiceConfig = scpr - ccr.cc.updateResolverState(ccr.curState, nil) + ccr.serializerScheduleLocked(func(_ context.Context) { + channelz.Infof(logger, ccr.channelzID, "ccResolverWrapper: got new service config: %s", sc) + if ccr.ignoreServiceConfig { + channelz.Info(logger, ccr.channelzID, "Service config lookups disabled; ignoring config") + return + } + scpr := parseServiceConfig(sc) + if scpr.Err != nil { + channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err) + return + } + ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) + ccr.curState.ServiceConfig = scpr + ccr.cc.updateResolverState(ccr.curState, nil) + }) } +// ParseServiceConfig is called by resolver implementations to parse a JSON +// representation of the service config. func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult { return parseServiceConfig(scJSON) } +// addChannelzTraceEvent adds a channelz trace event containing the new +// state received from resolver implementations. func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) { var updates []string var oldSC, newSC *ServiceConfig @@ -172,5 +235,5 @@ func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) { } else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 { updates = append(updates, "resolver returned new addresses") } - channelz.Infof(logger, ccr.cc.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; ")) + channelz.Infof(logger, ccr.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; ")) } diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 087b9ad7c..8869cc906 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -43,7 +43,6 @@ import ( "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/transport" @@ -116,12 +115,6 @@ type serviceInfo struct { mdata interface{} } -type serverWorkerData struct { - st transport.ServerTransport - wg *sync.WaitGroup - stream *transport.Stream -} - // Server is a gRPC server to serve RPC requests. type Server struct { opts serverOptions @@ -146,7 +139,7 @@ type Server struct { channelzID *channelz.Identifier czData *channelzData - serverWorkerChannels []chan *serverWorkerData + serverWorkerChannel chan func() } type serverOptions struct { @@ -178,6 +171,7 @@ type serverOptions struct { } var defaultServerOptions = serverOptions{ + maxConcurrentStreams: math.MaxUint32, maxReceiveMessageSize: defaultServerMaxReceiveMessageSize, maxSendMessageSize: defaultServerMaxSendMessageSize, connectionTimeout: 120 * time.Second, @@ -388,6 +382,9 @@ func MaxSendMsgSize(m int) ServerOption { // MaxConcurrentStreams returns a ServerOption that will apply a limit on the number // of concurrent streams to each ServerTransport. func MaxConcurrentStreams(n uint32) ServerOption { + if n == 0 { + n = math.MaxUint32 + } return newFuncServerOption(func(o *serverOptions) { o.maxConcurrentStreams = n }) @@ -561,40 +558,33 @@ func NumStreamWorkers(numServerWorkers uint32) ServerOption { const serverWorkerResetThreshold = 1 << 16 // serverWorkers blocks on a *transport.Stream channel forever and waits for -// data to be fed by serveStreams. This allows different requests to be +// data to be fed by serveStreams. This allows multiple requests to be // processed by the same goroutine, removing the need for expensive stack // re-allocations (see the runtime.morestack problem [1]). // // [1] https://github.com/golang/go/issues/18138 -func (s *Server) serverWorker(ch chan *serverWorkerData) { - // To make sure all server workers don't reset at the same time, choose a - // random number of iterations before resetting. - threshold := serverWorkerResetThreshold + grpcrand.Intn(serverWorkerResetThreshold) - for completed := 0; completed < threshold; completed++ { - data, ok := <-ch +func (s *Server) serverWorker() { + for completed := 0; completed < serverWorkerResetThreshold; completed++ { + f, ok := <-s.serverWorkerChannel if !ok { return } - s.handleStream(data.st, data.stream, s.traceInfo(data.st, data.stream)) - data.wg.Done() + f() } - go s.serverWorker(ch) + go s.serverWorker() } -// initServerWorkers creates worker goroutines and channels to process incoming +// initServerWorkers creates worker goroutines and a channel to process incoming // connections to reduce the time spent overall on runtime.morestack. func (s *Server) initServerWorkers() { - s.serverWorkerChannels = make([]chan *serverWorkerData, s.opts.numServerWorkers) + s.serverWorkerChannel = make(chan func()) for i := uint32(0); i < s.opts.numServerWorkers; i++ { - s.serverWorkerChannels[i] = make(chan *serverWorkerData) - go s.serverWorker(s.serverWorkerChannels[i]) + go s.serverWorker() } } func (s *Server) stopServerWorkers() { - for i := uint32(0); i < s.opts.numServerWorkers; i++ { - close(s.serverWorkerChannels[i]) - } + close(s.serverWorkerChannel) } // NewServer creates a gRPC server which has no service registered and has not @@ -898,7 +888,7 @@ func (s *Server) drainServerTransports(addr string) { s.mu.Lock() conns := s.conns[addr] for st := range conns { - st.Drain() + st.Drain("") } s.mu.Unlock() } @@ -946,26 +936,26 @@ func (s *Server) serveStreams(st transport.ServerTransport) { defer st.Close(errors.New("finished serving streams for the server transport")) var wg sync.WaitGroup - var roundRobinCounter uint32 + streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams) st.HandleStreams(func(stream *transport.Stream) { wg.Add(1) + + streamQuota.acquire() + f := func() { + defer streamQuota.release() + defer wg.Done() + s.handleStream(st, stream, s.traceInfo(st, stream)) + } + if s.opts.numServerWorkers > 0 { - data := &serverWorkerData{st: st, wg: &wg, stream: stream} select { - case s.serverWorkerChannels[atomic.AddUint32(&roundRobinCounter, 1)%s.opts.numServerWorkers] <- data: + case s.serverWorkerChannel <- f: + return default: // If all stream workers are busy, fallback to the default code path. - go func() { - s.handleStream(st, stream, s.traceInfo(st, stream)) - wg.Done() - }() } - } else { - go func() { - defer wg.Done() - s.handleStream(st, stream, s.traceInfo(st, stream)) - }() } + go f() }, func(ctx context.Context, method string) context.Context { if !EnableTracing { return ctx @@ -1054,7 +1044,7 @@ func (s *Server) addConn(addr string, st transport.ServerTransport) bool { if s.drain { // Transport added after we drained our existing conns: drain it // immediately. - st.Drain() + st.Drain("") } if s.conns[addr] == nil { @@ -1864,7 +1854,7 @@ func (s *Server) GracefulStop() { if !s.drain { for _, conns := range s.conns { for st := range conns { - st.Drain() + st.Drain("graceful_stop") } } s.drain = true @@ -2060,3 +2050,32 @@ func validateSendCompressor(name, clientCompressors string) error { } return fmt.Errorf("client does not support compressor %q", name) } + +// atomicSemaphore implements a blocking, counting semaphore. acquire should be +// called synchronously; release may be called asynchronously. +type atomicSemaphore struct { + n int64 + wait chan struct{} +} + +func (q *atomicSemaphore) acquire() { + if atomic.AddInt64(&q.n, -1) < 0 { + // We ran out of quota. Block until a release happens. + <-q.wait + } +} + +func (q *atomicSemaphore) release() { + // N.B. the "<= 0" check below should allow for this to work with multiple + // concurrent calls to acquire, but also note that with synchronous calls to + // acquire, as our system does, n will never be less than -1. There are + // fairness issues (queuing) to consider if this was to be generalized. + if atomic.AddInt64(&q.n, 1) <= 0 { + // An acquire was waiting on us. Unblock it. + q.wait <- struct{}{} + } +} + +func newHandlerQuota(n uint32) *atomicSemaphore { + return &atomicSemaphore{n: int64(n), wait: make(chan struct{}, 1)} +} diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go index f22acace4..0df11fc09 100644 --- a/vendor/google.golang.org/grpc/service_config.go +++ b/vendor/google.golang.org/grpc/service_config.go @@ -23,8 +23,6 @@ import ( "errors" "fmt" "reflect" - "strconv" - "strings" "time" "google.golang.org/grpc/codes" @@ -106,8 +104,8 @@ type healthCheckConfig struct { type jsonRetryPolicy struct { MaxAttempts int - InitialBackoff string - MaxBackoff string + InitialBackoff internalserviceconfig.Duration + MaxBackoff internalserviceconfig.Duration BackoffMultiplier float64 RetryableStatusCodes []codes.Code } @@ -129,50 +127,6 @@ type retryThrottlingPolicy struct { TokenRatio float64 } -func parseDuration(s *string) (*time.Duration, error) { - if s == nil { - return nil, nil - } - if !strings.HasSuffix(*s, "s") { - return nil, fmt.Errorf("malformed duration %q", *s) - } - ss := strings.SplitN((*s)[:len(*s)-1], ".", 3) - if len(ss) > 2 { - return nil, fmt.Errorf("malformed duration %q", *s) - } - // hasDigits is set if either the whole or fractional part of the number is - // present, since both are optional but one is required. - hasDigits := false - var d time.Duration - if len(ss[0]) > 0 { - i, err := strconv.ParseInt(ss[0], 10, 32) - if err != nil { - return nil, fmt.Errorf("malformed duration %q: %v", *s, err) - } - d = time.Duration(i) * time.Second - hasDigits = true - } - if len(ss) == 2 && len(ss[1]) > 0 { - if len(ss[1]) > 9 { - return nil, fmt.Errorf("malformed duration %q", *s) - } - f, err := strconv.ParseInt(ss[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("malformed duration %q: %v", *s, err) - } - for i := 9; i > len(ss[1]); i-- { - f *= 10 - } - d += time.Duration(f) - hasDigits = true - } - if !hasDigits { - return nil, fmt.Errorf("malformed duration %q", *s) - } - - return &d, nil -} - type jsonName struct { Service string Method string @@ -201,7 +155,7 @@ func (j jsonName) generatePath() (string, error) { type jsonMC struct { Name *[]jsonName WaitForReady *bool - Timeout *string + Timeout *internalserviceconfig.Duration MaxRequestMessageBytes *int64 MaxResponseMessageBytes *int64 RetryPolicy *jsonRetryPolicy @@ -252,15 +206,10 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult { if m.Name == nil { continue } - d, err := parseDuration(m.Timeout) - if err != nil { - logger.Warningf("grpc: unmarshaling service config %s: %v", js, err) - return &serviceconfig.ParseResult{Err: err} - } mc := MethodConfig{ WaitForReady: m.WaitForReady, - Timeout: d, + Timeout: (*time.Duration)(m.Timeout), } if mc.RetryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil { logger.Warningf("grpc: unmarshaling service config %s: %v", js, err) @@ -312,18 +261,10 @@ func convertRetryPolicy(jrp *jsonRetryPolicy) (p *internalserviceconfig.RetryPol if jrp == nil { return nil, nil } - ib, err := parseDuration(&jrp.InitialBackoff) - if err != nil { - return nil, err - } - mb, err := parseDuration(&jrp.MaxBackoff) - if err != nil { - return nil, err - } if jrp.MaxAttempts <= 1 || - *ib <= 0 || - *mb <= 0 || + jrp.InitialBackoff <= 0 || + jrp.MaxBackoff <= 0 || jrp.BackoffMultiplier <= 0 || len(jrp.RetryableStatusCodes) == 0 { logger.Warningf("grpc: ignoring retry policy %v due to illegal configuration", jrp) @@ -332,8 +273,8 @@ func convertRetryPolicy(jrp *jsonRetryPolicy) (p *internalserviceconfig.RetryPol rp := &internalserviceconfig.RetryPolicy{ MaxAttempts: jrp.MaxAttempts, - InitialBackoff: *ib, - MaxBackoff: *mb, + InitialBackoff: time.Duration(jrp.InitialBackoff), + MaxBackoff: time.Duration(jrp.MaxBackoff), BackoffMultiplier: jrp.BackoffMultiplier, RetryableStatusCodes: make(map[codes.Code]bool), } diff --git a/vendor/google.golang.org/grpc/status/status.go b/vendor/google.golang.org/grpc/status/status.go index 623be39f2..bcf2e4d81 100644 --- a/vendor/google.golang.org/grpc/status/status.go +++ b/vendor/google.golang.org/grpc/status/status.go @@ -77,9 +77,18 @@ func FromProto(s *spb.Status) *Status { // FromError returns a Status representation of err. // // - If err was produced by this package or implements the method `GRPCStatus() -// *Status`, the appropriate Status is returned. +// *Status` and `GRPCStatus()` does not return nil, or if err wraps a type +// satisfying this, the Status from `GRPCStatus()` is returned. For wrapped +// errors, the message returned contains the entire err.Error() text and not +// just the wrapped status. In that case, ok is true. // -// - If err is nil, a Status is returned with codes.OK and no message. +// - If err is nil, a Status is returned with codes.OK and no message, and ok +// is true. +// +// - If err implements the method `GRPCStatus() *Status` and `GRPCStatus()` +// returns nil (which maps to Codes.OK), or if err wraps a type +// satisfying this, a Status is returned with codes.Unknown and err's +// Error() message, and ok is false. // // - Otherwise, err is an error not compatible with this package. In this // case, a Status is returned with codes.Unknown and err's Error() message, @@ -88,10 +97,29 @@ func FromError(err error) (s *Status, ok bool) { if err == nil { return nil, true } - if se, ok := err.(interface { - GRPCStatus() *Status - }); ok { - return se.GRPCStatus(), true + type grpcstatus interface{ GRPCStatus() *Status } + if gs, ok := err.(grpcstatus); ok { + if gs.GRPCStatus() == nil { + // Error has status nil, which maps to codes.OK. There + // is no sensible behavior for this, so we turn it into + // an error with codes.Unknown and discard the existing + // status. + return New(codes.Unknown, err.Error()), false + } + return gs.GRPCStatus(), true + } + var gs grpcstatus + if errors.As(err, &gs) { + if gs.GRPCStatus() == nil { + // Error wraps an error that has status nil, which maps + // to codes.OK. There is no sensible behavior for this, + // so we turn it into an error with codes.Unknown and + // discard the existing status. + return New(codes.Unknown, err.Error()), false + } + p := gs.GRPCStatus().Proto() + p.Message = err.Error() + return status.FromProto(p), true } return New(codes.Unknown, err.Error()), false } @@ -103,19 +131,16 @@ func Convert(err error) *Status { return s } -// Code returns the Code of the error if it is a Status error, codes.OK if err -// is nil, or codes.Unknown otherwise. +// Code returns the Code of the error if it is a Status error or if it wraps a +// Status error. If that is not the case, it returns codes.OK if err is nil, or +// codes.Unknown otherwise. func Code(err error) codes.Code { // Don't use FromError to avoid allocation of OK status. if err == nil { return codes.OK } - if se, ok := err.(interface { - GRPCStatus() *Status - }); ok { - return se.GRPCStatus().Code() - } - return codes.Unknown + + return Convert(err).Code() } // FromContextError converts a context error or wrapped context error into a diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index d1226a412..10092685b 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -123,6 +123,9 @@ type ClientStream interface { // calling RecvMsg on the same stream at the same time, but it is not safe // to call SendMsg on the same stream in different goroutines. It is also // not safe to call CloseSend concurrently with SendMsg. + // + // It is not safe to modify the message after calling SendMsg. Tracing + // libraries and stats handlers may use the message lazily. SendMsg(m interface{}) error // RecvMsg blocks until it receives a message into m or the stream is // done. It returns io.EOF when the stream completes successfully. On @@ -152,6 +155,11 @@ type ClientStream interface { // If none of the above happen, a goroutine and a context will be leaked, and grpc // will not call the optionally-configured stats handler with a stats.End message. func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { + if err := cc.idlenessMgr.onCallBegin(); err != nil { + return nil, err + } + defer cc.idlenessMgr.onCallEnd() + // allow interceptor to see all applicable call options, which means those // configured as defaults from dial option as well as per-call options opts = combine(cc.dopts.callOptions, opts) @@ -469,7 +477,7 @@ func (a *csAttempt) newStream() error { // It is safe to overwrite the csAttempt's context here, since all state // maintained in it are local to the attempt. When the attempt has to be // retried, a new instance of csAttempt will be created. - if a.pickResult.Metatada != nil { + if a.pickResult.Metadata != nil { // We currently do not have a function it the metadata package which // merges given metadata with existing metadata in a context. Existing // function `AppendToOutgoingContext()` takes a variadic argument of key @@ -479,7 +487,7 @@ func (a *csAttempt) newStream() error { // in a form passable to AppendToOutgoingContext(), or create a version // of AppendToOutgoingContext() that accepts a metadata.MD. md, _ := metadata.FromOutgoingContext(a.ctx) - md = metadata.Join(md, a.pickResult.Metatada) + md = metadata.Join(md, a.pickResult.Metadata) a.ctx = metadata.NewOutgoingContext(a.ctx, md) } @@ -1265,14 +1273,19 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin as.p = &parser{r: s} ac.incrCallsStarted() if desc != unaryStreamDesc { - // Listen on cc and stream contexts to cleanup when the user closes the - // ClientConn or cancels the stream context. In all other cases, an error - // should already be injected into the recv buffer by the transport, which - // the client will eventually receive, and then we will cancel the stream's - // context in clientStream.finish. + // Listen on stream context to cleanup when the stream context is + // canceled. Also listen for the addrConn's context in case the + // addrConn is closed or reconnects to a different address. In all + // other cases, an error should already be injected into the recv + // buffer by the transport, which the client will eventually receive, + // and then we will cancel the stream's context in + // addrConnStream.finish. go func() { + ac.mu.Lock() + acCtx := ac.ctx + ac.mu.Unlock() select { - case <-ac.ctx.Done(): + case <-acCtx.Done(): as.finish(status.Error(codes.Canceled, "grpc: the SubConn is closing")) case <-ctx.Done(): as.finish(toRPCErr(ctx.Err())) diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 3c6e3c911..3cc754062 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.54.0" +const Version = "1.56.3" diff --git a/vendor/modules.txt b/vendor/modules.txt index 7ac34b8ba..2446748ef 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -166,9 +166,11 @@ github.com/gogo/protobuf/gogoproto github.com/gogo/protobuf/proto github.com/gogo/protobuf/protoc-gen-gogo/descriptor github.com/gogo/protobuf/sortkeys -# github.com/golang/glog v1.0.0 -## explicit; go 1.11 +# github.com/golang/glog v1.1.0 +## explicit; go 1.18 github.com/golang/glog +github.com/golang/glog/internal/logsink +github.com/golang/glog/internal/stackdump # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da ## explicit github.com/golang/groupcache/lru @@ -498,8 +500,8 @@ go.uber.org/zap/zapcore # go4.org v0.0.0-20200104003542-c7e774b10ea0 ## explicit go4.org/errorutil -# golang.org/x/crypto v0.14.0 -## explicit; go 1.17 +# golang.org/x/crypto v0.17.0 +## explicit; go 1.18 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish golang.org/x/crypto/pbkdf2 @@ -533,17 +535,17 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.4.0 ## explicit; go 1.17 golang.org/x/sync/errgroup -# golang.org/x/sys v0.13.0 -## explicit; go 1.17 +# golang.org/x/sys v0.15.0 +## explicit; go 1.18 golang.org/x/sys/execabs golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.13.0 -## explicit; go 1.17 +# golang.org/x/term v0.15.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 @@ -597,7 +599,7 @@ google.golang.org/appengine/urlfetch # google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 ## explicit; go 1.19 google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.54.0 +# google.golang.org/grpc v1.56.3 ## explicit; go 1.17 google.golang.org/grpc google.golang.org/grpc/attributes