diff --git a/Dockerfile b/Dockerfile index ac5107ba5..078749098 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,9 +12,10 @@ RUN go mod download COPY . /go/node-feature-discovery ARG NFD_VERSION +ARG HOSTMOUNT_PREFIX RUN go install \ - -ldflags "-s -w -X sigs.k8s.io/node-feature-discovery/pkg/version.version=$NFD_VERSION" \ + -ldflags "-s -w -X sigs.k8s.io/node-feature-discovery/pkg/version.version=$NFD_VERSION -X sigs.k8s.io/node-feature-discovery/source.pathPrefix=$HOSTMOUNT_PREFIX" \ ./cmd/* RUN install -D -m644 nfd-worker.conf.example /etc/kubernetes/node-feature-discovery/nfd-worker.conf diff --git a/Makefile b/Makefile index 6b09a4311..989489c62 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ IMAGE_TAG_NAME := $(VERSION) IMAGE_REPO := $(IMAGE_REGISTRY)/$(IMAGE_NAME) IMAGE_TAG := $(IMAGE_REPO):$(IMAGE_TAG_NAME) K8S_NAMESPACE := kube-system +HOSTMOUNT_PREFIX := /host- KUBECONFIG := E2E_TEST_CONFIG := @@ -26,6 +27,7 @@ all: image image: yamls $(IMAGE_BUILD_CMD) --build-arg NFD_VERSION=$(VERSION) \ + --build-arg HOSTMOUNT_PREFIX=$(HOSTMOUNT_PREFIX) \ -t $(IMAGE_TAG) \ $(IMAGE_BUILD_EXTRA_OPTS) ./ @@ -38,6 +40,7 @@ yamls: $(yaml_instances) -e s',^(\s*)name: node-feature-discovery # NFD namespace,\1name: ${K8S_NAMESPACE},' \ -e s',^(\s*)image:.+$$,\1image: ${IMAGE_TAG},' \ -e s',^(\s*)namespace:.+$$,\1namespace: ${K8S_NAMESPACE},' \ + -e s',^(\s*)mountPath: "/host-,\1mountPath: "${HOSTMOUNT_PREFIX},' \ $< > $@ mock: diff --git a/source/config.go b/source/config.go new file mode 100644 index 000000000..1047c4314 --- /dev/null +++ b/source/config.go @@ -0,0 +1,39 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package source + +import ( + "path/filepath" +) + +var ( + pathPrefix = "/" + // BootPath is where the /boot directory of the system to be inspected is located + BootDir = HostDir(pathPrefix + "boot") + // EtcPath is where the /etc directory of the system to be inspected is located + EtcDir = HostDir(pathPrefix + "etc") + // SysfsPath is where the /sys directory of the system to be inspected is located + SysfsDir = HostDir(pathPrefix + "sys") +) + +// HostDir is a helper for handling host system directories +type HostDir string + +// Path returns a full path to a file under HostDir +func (d HostDir) Path(elem ...string) string { + return filepath.Join(append([]string{string(d)}, elem...)...) +} diff --git a/source/cpu/cpu.go b/source/cpu/cpu.go index 7244155a2..1c654273e 100644 --- a/source/cpu/cpu.go +++ b/source/cpu/cpu.go @@ -19,15 +19,10 @@ package cpu import ( "io/ioutil" "log" - "path" "sigs.k8s.io/node-feature-discovery/source" ) -const ( - cpuDevicesBaseDir = "/sys/bus/cpu/devices" -) - // Configuration file options type cpuidConfig struct { AttributeBlacklist []string `json:"attributeBlacklist,omitempty"` @@ -137,14 +132,15 @@ func (s Source) Discover() (source.Features, error) { // Check if any (online) CPUs have thread siblings func haveThreadSiblings() (bool, error) { - files, err := ioutil.ReadDir(cpuDevicesBaseDir) + + files, err := ioutil.ReadDir(source.SysfsDir.Path("bus/cpu/devices")) if err != nil { return false, err } for _, file := range files { // Try to read siblings from topology - siblings, err := ioutil.ReadFile(path.Join(cpuDevicesBaseDir, file.Name(), "topology/thread_siblings_list")) + siblings, err := ioutil.ReadFile(source.SysfsDir.Path("bus/cpu/devices", file.Name(), "topology/thread_siblings_list")) if err != nil { return false, err } diff --git a/source/cpu/power_amd64.go b/source/cpu/power_amd64.go index 0b23e93ec..94005fc9f 100644 --- a/source/cpu/power_amd64.go +++ b/source/cpu/power_amd64.go @@ -20,11 +20,11 @@ import ( "fmt" "io/ioutil" "os" - "path" "strconv" "strings" "sigs.k8s.io/node-feature-discovery/pkg/cpuid" + "sigs.k8s.io/node-feature-discovery/source" ) const ( @@ -38,13 +38,14 @@ func discoverSSTBF() (bool, error) { nominalBaseFrequency := int(freqInfo.EAX) // Loop over all CPUs in the system - files, err := ioutil.ReadDir(cpuDevicesBaseDir) + files, err := ioutil.ReadDir(source.SysfsDir.Path("bus/cpu/devices")) + if err != nil { return false, err } for _, file := range files { // Try to read effective base frequency of each cpu in the system - filePath := path.Join(cpuDevicesBaseDir, file.Name(), "cpufreq/base_frequency") + filePath := source.SysfsDir.Path("bus/cpu/devices", file.Name(), "cpufreq/base_frequency") data, err := ioutil.ReadFile(filePath) if os.IsNotExist(err) { // Ignore missing file and continue to check other CPUs diff --git a/source/cpu/pstate.go b/source/cpu/pstate.go index 550ebaedc..9b8c1e023 100644 --- a/source/cpu/pstate.go +++ b/source/cpu/pstate.go @@ -20,6 +20,8 @@ import ( "fmt" "io/ioutil" "runtime" + + "sigs.k8s.io/node-feature-discovery/source" ) // Discover p-state related features such as turbo boost. @@ -31,7 +33,7 @@ func detectPstate() (map[string]string, error) { } // Only looking for turbo boost for now... - bytes, err := ioutil.ReadFile("/sys/devices/system/cpu/intel_pstate/no_turbo") + bytes, err := ioutil.ReadFile(source.SysfsDir.Path("devices/system/cpu/intel_pstate/no_turbo")) if err != nil { return nil, fmt.Errorf("can't detect whether turbo boost is enabled: %s", err.Error()) } diff --git a/source/internal/pci_utils.go b/source/internal/pci_utils.go index a2794aa32..067b2a8ec 100644 --- a/source/internal/pci_utils.go +++ b/source/internal/pci_utils.go @@ -22,6 +22,8 @@ import ( "log" "path" "strings" + + "sigs.k8s.io/node-feature-discovery/source" ) type PciDeviceInfo map[string]string @@ -72,10 +74,10 @@ func readPciDevInfo(devPath string, deviceAttrSpec map[string]bool) (PciDeviceIn // "class" attribute is considered mandatory. // DetectPci() will fail if the retrieval of a mandatory attribute fails. func DetectPci(deviceAttrSpec map[string]bool) (map[string][]PciDeviceInfo, error) { - const basePath = "/sys/bus/pci/devices/" + sysfsBasePath := source.SysfsDir.Path("bus/pci/devices") devInfo := make(map[string][]PciDeviceInfo) - devices, err := ioutil.ReadDir(basePath) + devices, err := ioutil.ReadDir(sysfsBasePath) if err != nil { return nil, err } @@ -84,7 +86,7 @@ func DetectPci(deviceAttrSpec map[string]bool) (map[string][]PciDeviceInfo, erro // Iterate over devices for _, device := range devices { - info, err := readPciDevInfo(path.Join(basePath, device.Name()), deviceAttrSpec) + info, err := readPciDevInfo(path.Join(sysfsBasePath, device.Name()), deviceAttrSpec) if err != nil { log.Print(err) continue diff --git a/source/iommu/iommu.go b/source/iommu/iommu.go index 3f90d62fe..9dfe0080a 100644 --- a/source/iommu/iommu.go +++ b/source/iommu/iommu.go @@ -32,7 +32,7 @@ func (s Source) Discover() (source.Features, error) { features := source.Features{} // Check if any iommu devices are available - devices, err := ioutil.ReadDir("/sys/class/iommu/") + devices, err := ioutil.ReadDir(source.SysfsDir.Path("class/iommu/")) if err != nil { return nil, fmt.Errorf("Failed to check for IOMMU support: %v", err) } diff --git a/source/kernel/kernel.go b/source/kernel/kernel.go index e2b9a34e0..37c301c77 100644 --- a/source/kernel/kernel.go +++ b/source/kernel/kernel.go @@ -167,7 +167,7 @@ func parseKconfig() (map[string]string, error) { return nil, err } // Read kconfig - raw, err = ioutil.ReadFile("/host-boot/config-" + uname) + raw, err = ioutil.ReadFile(source.BootDir.Path("config-" + uname)) if err != nil { return nil, err } diff --git a/source/kernel/selinux.go b/source/kernel/selinux.go index bf34ebe7d..9a67d3b45 100644 --- a/source/kernel/selinux.go +++ b/source/kernel/selinux.go @@ -19,11 +19,13 @@ package kernel import ( "fmt" "io/ioutil" + + "sigs.k8s.io/node-feature-discovery/source" ) // Detect if selinux has been enabled in the kernel func SelinuxEnabled() (bool, error) { - status, err := ioutil.ReadFile("/host-sys/fs/selinux/enforce") + status, err := ioutil.ReadFile(source.SysfsDir.Path("fs/selinux/enforce")) if err != nil { return false, fmt.Errorf("Failed to detect the status of selinux, please check if the system supports selinux and make sure /sys on the host is mounted into the container: %s", err.Error()) } diff --git a/source/memory/memory.go b/source/memory/memory.go index c1a769df0..0e5e6d685 100644 --- a/source/memory/memory.go +++ b/source/memory/memory.go @@ -60,7 +60,7 @@ func (s Source) Discover() (source.Features, error) { func isNuma() (bool, error) { // Find out how many nodes are online // Multiple nodes is a sign of NUMA - bytes, err := ioutil.ReadFile("/sys/devices/system/node/online") + bytes, err := ioutil.ReadFile(source.SysfsDir.Path("devices/system/node/online")) if err != nil { return false, err } @@ -81,7 +81,7 @@ func detectNvdimm() (map[string]bool, error) { features := make(map[string]bool) // Check presence of physical devices - devices, err := ioutil.ReadDir("/sys/class/nd/") + devices, err := ioutil.ReadDir(source.SysfsDir.Path("class/nd")) if err == nil { if len(devices) > 0 { features["present"] = true @@ -93,7 +93,7 @@ func detectNvdimm() (map[string]bool, error) { } // Check presence of DAX-configured regions - devices, err = ioutil.ReadDir("/sys/bus/nd/devices/") + devices, err = ioutil.ReadDir(source.SysfsDir.Path("bus/nd/devices")) if err == nil { for _, d := range devices { if strings.HasPrefix(d.Name(), "dax") { diff --git a/source/network/network.go b/source/network/network.go index 63bf9c854..58fb1a4b8 100644 --- a/source/network/network.go +++ b/source/network/network.go @@ -44,8 +44,7 @@ func (s Source) Discover() (source.Features, error) { // iterating through network interfaces to obtain their respective number of virtual functions for _, netInterface := range netInterfaces { if strings.Contains(netInterface.Flags.String(), "up") && !strings.Contains(netInterface.Flags.String(), "loopback") { - totalVfsPath := "/sys/class/net/" + netInterface.Name + "/device/sriov_totalvfs" - totalBytes, err := ioutil.ReadFile(totalVfsPath) + totalBytes, err := ioutil.ReadFile(source.SysfsDir.Path("class/net", netInterface.Name, "device/sriov_totalvfs")) if err != nil { log.Printf("SR-IOV not supported for network interface: %s: %v", netInterface.Name, err) continue @@ -60,8 +59,7 @@ func (s Source) Discover() (source.Features, error) { log.Printf("SR-IOV capability is detected on the network interface: %s", netInterface.Name) log.Printf("%d maximum supported number of virtual functions on network interface: %s", t, netInterface.Name) features["sriov.capable"] = true - numVfsPath := "/sys/class/net/" + netInterface.Name + "/device/sriov_numvfs" - numBytes, err := ioutil.ReadFile(numVfsPath) + numBytes, err := ioutil.ReadFile(source.SysfsDir.Path("class/net", netInterface.Name, "device/sriov_numvfs")) if err != nil { log.Printf("SR-IOV not configured for network interface: %s: %s", netInterface.Name, err) continue diff --git a/source/storage/storage.go b/source/storage/storage.go index 77f15283d..4e385df09 100644 --- a/source/storage/storage.go +++ b/source/storage/storage.go @@ -34,10 +34,10 @@ func (s Source) Discover() (source.Features, error) { features := source.Features{} // Check if there is any non-rotational block devices attached to the node - blockdevices, err := ioutil.ReadDir("/sys/block/") + blockdevices, err := ioutil.ReadDir(source.SysfsDir.Path("block")) if err == nil { for _, bdev := range blockdevices { - fname := "/sys/block/" + bdev.Name() + "/queue/rotational" + fname := source.SysfsDir.Path("block", bdev.Name(), "queue/rotational") bytes, err := ioutil.ReadFile(fname) if err != nil { return nil, fmt.Errorf("can't read rotational status: %s", err.Error()) diff --git a/source/system/system.go b/source/system/system.go index 750a5d824..dab3ce89e 100644 --- a/source/system/system.go +++ b/source/system/system.go @@ -66,7 +66,7 @@ func (s Source) Discover() (source.Features, error) { func parseOSRelease() (map[string]string, error) { release := map[string]string{} - f, err := os.Open("/host-etc/os-release") + f, err := os.Open(source.EtcDir.Path("os-release")) if err != nil { return nil, err }