1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-14 11:57:51 +00:00

Replayed commit history.

This commit is contained in:
Connor Doyle 2016-08-30 17:04:43 -07:00
commit 570d1ae5be
18 changed files with 1303 additions and 2 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
node-feature-discovery
node-feature-discovery-job.json
vendor/
intel-cmt-cat/
rdt-discovery/l2-alloc-discovery
rdt-discovery/l3-alloc-discovery
rdt-discovery/mon-discovery

7
.travis.yml Normal file
View file

@ -0,0 +1,7 @@
sudo: required
services:
- docker
script:
- docker build -t intelsdi/dbi-iafeature-discovery .

16
Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM golang:1.6
ADD . /go/src/github.com/kubernetes-incubator/node-feature-discovery
WORKDIR /go/src/github.com/kubernetes-incubator/node-feature-discovery
RUN git clone --depth 1 https://github.com/01org/intel-cmt-cat.git
RUN cd intel-cmt-cat/lib; make install
RUN cd rdt-discovery; make
RUN go get github.com/Masterminds/glide
RUN glide install
RUN go install \
-ldflags "-s -w -X main.version=`git describe --tags --dirty --always`" \
github.com/kubernetes-incubator/node-feature-discovery
ENTRYPOINT ["/go/bin/node-feature-discovery"]

13
Makefile Normal file
View file

@ -0,0 +1,13 @@
.PHONY: all
DOCKER_REGISTRY_USER := kubernetesincubator
DOCKER_IMAGE_NAME := node-feature-discovery
VERSION := $(shell git describe --tags --dirty --always)
all: docker
# To override DOCKER_REGISTRY_USER use the -e option as follows:
# DOCKER_REGISTRY_USER=<my-username> make docker -e
docker:
docker build -t $(DOCKER_REGISTRY_USER)/$(DOCKER_IMAGE_NAME):$(VERSION) ./

147
README.md
View file

@ -1,6 +1,16 @@
# Node feature discovery for [Kubernetes](https://kubernetes.io)
- [Overview](#overview)
- [Command line interface](#command-line-interface)
- [Feature discovery](#feature-discovery)
- [Feature sources](#feature-sources)
- [Feature labels](#feature-labels)
- [Getting started](#getting-started)
- [System requirements](#system-requirements)
- [Usage](#usage)
- [Building from source](#building-from-source)
- [Targeting nodes with specific features](#targeting-nodes-with-specific-features)
- [References](#references)
- [License](#license)
## Overview
@ -9,8 +19,29 @@ This software enables node feature discovery for Kubernetes. It detects
hardware features available on each node in a Kubernetes cluster, and advertises
those features using node labels.
## Command line interface
```
node-feature-discovery.
Usage:
node-feature-discovery [--no-publish --sources=<sources>]
node-feature-discovery -h | --help
node-feature-discovery --version
Options:
-h --help Show this screen.
--version Output version and exit.
--sources=<sources> Comma separated list of feature sources. [Default: cpuid,rdt,pstate]
--no-publish Do not publish discovered features to the cluster-local Kubernetes API server.
```
## Feature discovery
### Feature sources
The current set of feature sources are the following:
- [CPUID][cpuid] for x86 CPU details
- [Intel Resource Director Technology][intel-rdt]
- [Intel P-State driver][intel-pstate]
@ -31,14 +62,120 @@ the only label value published for features is the string `"true"`._
```json
{
"node.alpha.intel.com/dbi-iafeature-discovery.version": "v0.1.0",
"node.alpha.intel.com/node-feature-discovery.version": "v0.1.0",
"node.alpha.intel.com/v0.1.0-cpuid-<feature-name>": "true",
"node.alpha.intel.com/v0.1.0-rdt-<feature-name>": "true",
"node.alpha.intel.com/v0.1.0-pstate-<feature-name>": "true"
}
```
### References
The `--sources` flag controls which sources to use for discovery.
### Intel Resource Director Technology (RDT) Features
| Feature name | Description |
| :------------: | :---------------------------------------------------------------------------------: |
| RDTMON | Intel Cache Monitoring Technology (CMT) and Intel Memory Bandwidth Monitoring (MBM)
| RDTL3CA | Intel L3 Cache Allocation Technology
| RDTL2CA | Intel L2 Cache Allocation Technology
### CPUID Features (Partial List)
| Feature name | Description |
| :------------: | :----------------------------------------------------------: |
| ADX | Multi-Precision Add-Carry Instruction Extensions (ADX)
| AESNI | Advanced Encryption Standard (AES) New Instructions (AES-NI)
| AVX | Advanced Vector Extensions (AVX)
| AVX2 | Advanced Vector Extensions 2 (AVX2)
| BMI1 | Bit Manipulation Instruction Set 1 (BMI)
| BMI2 | Bit Manipulation Instruction Set 2 (BMI2)
| SSE4.1 | Streaming SIMD Extensions 4.1 (SSE4.1)
| SSE4.2 | Streaming SIMD Extensions 4.2 (SSE4.2)
| SGX | Software Guard Extensions (SGX)
### System Requirements
1. Linux (x86_64)
1. [kubectl] [kubectl-setup] (properly set up and configured to work with your
Kubernetes cluster)
1. [Docker] [docker-down] (only required to build and push docker images)
### Usage
Feature discovery is done as a one-shot job. There is an example script in this
repo that demonstrates how to deploy the job to unlabeled nodes.
```
./label-nodes.sh
```
The discovery script will launch a job on each each unlabeled node in the
cluster. When the job runs, it contacts the Kubernetes API server to add labels
to the node to advertise hardware features (initially, from `cpuid` and RDT).
## Building from source
Download the source code.
```
git clone https://github.com/kubernetes-incubator/node-feature-discovery
```
**Build the Docker image:**
```
cd <project-root>
make
```
**NOTE: To override the `DOCKER_REGISTRY_USER` use the `-e` option as follows:
`DOCKER_REGISTRY_USER=<my-username> make docker -e`**
Push the Docker Image (optional)
```
docker push <registry-user>/<image-name>:<version>
```
**Change the job spec to use your custom image (optional):**
To use your published image from the step above instead of the
`kubernetesincubator/node-feature-discovery` image, edit line 40 in the file
[node-feature-discovery-job.json.template](node-feature-discovery-job.json.template)
to the new location (`<registry-user>/<image-name>[:<version>]`).
## Targeting Nodes with Specific Features
Nodes with specific features can be targeted using the `nodeSelector` field. The
following example shows how to target nodes with Intel TurboBoost enabled.
```json
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"labels": {
"env": "test"
},
"name": "golang-test"
},
"spec": {
"containers": [
{
"image": "golang",
"name": "go1",
}
],
"nodeSelector": {
"node.alpha.intel.com/v0.1.0-pstate-turbo": "true"
}
}
}
```
For more details on targeting nodes, see [node selection][node-sel].
## References
Github issues
@ -48,6 +185,7 @@ Github issues
[Design proposal](https://docs.google.com/document/d/1uulT2AjqXjc_pLtDu0Kw9WyvvXm-WAZZaSiUziKsr68/edit)
## License
This is open source software released under the [Apache 2.0 License](LICENSE).
@ -56,3 +194,8 @@ This is open source software released under the [Apache 2.0 License](LICENSE).
[cpuid]: http://man7.org/linux/man-pages/man4/cpuid.4.html
[intel-rdt]: http://www.intel.com/content/www/us/en/architecture-and-technology/resource-director-technology.html
[intel-pstate]: https://www.kernel.org/doc/Documentation/cpu-freq/intel-pstate.txt
[docker-down]: https://docs.docker.com/engine/installation
[golang-down]: https://golang.org/dl
[gcc-down]: https://gcc.gnu.org
[kubectl-setup]: https://coreos.com/kubernetes/docs/latest/configure-kubectl.html
[node-sel]: http://kubernetes.io/docs/user-guide/node-selection

366
glide.lock generated Normal file
View file

@ -0,0 +1,366 @@
hash: 9943a35009296355c6a66485228855db99eefab65890c8fa89a4776cba9c9b71
updated: 2016-07-28T17:22:20.295749043-07:00
imports:
- name: github.com/beorn7/perks
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
subpackages:
- quantile
- name: github.com/blang/semver
version: 31b736133b98f26d5e078ec9eb591666edfd091f
- name: github.com/coreos/go-oidc
version: 5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b
subpackages:
- http
- jose
- key
- oauth2
- oidc
- name: github.com/coreos/go-systemd
version: 4484981625c1a6a2ecb40a390fcb6a9bcfee76e3
subpackages:
- activation
- daemon
- dbus
- journal
- unit
- util
- name: github.com/coreos/pkg
version: 7f080b6c11ac2d2347c3cd7521e810207ea1a041
subpackages:
- capnslog
- dlopen
- health
- httputil
- timeutil
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
subpackages:
- spew
- name: github.com/docker/distribution
version: cd27f179f2c10c5d300e6d09025b538c475b0d51
subpackages:
- digest
- reference
- name: github.com/docker/docker
version: 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d
subpackages:
- pkg/jsonmessage
- pkg/mount
- pkg/stdcopy
- pkg/symlink
- pkg/term
- pkg/term/winconsole
- pkg/timeutils
- pkg/units
- name: github.com/docker/go-units
version: 0bbddae09c5a5419a8c6dcdd7ff90da3d450393b
- name: github.com/docopt/docopt-go
version: 784ddc588536785e7299f7272f39101f7faccc3f
- name: github.com/docopt/docopt.go
version: 784ddc588536785e7299f7272f39101f7faccc3f
- name: github.com/emicklei/go-restful
version: 7c47e2558a0bbbaba9ecab06bc6681e73028a28a
subpackages:
- log
- swagger
- name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/gogo/protobuf
version: 82d16f734d6d871204a3feb1a73cb220cc92574c
subpackages:
- gogoproto
- plugin/defaultcheck
- plugin/description
- plugin/embedcheck
- plugin/enumstringer
- plugin/equal
- plugin/face
- plugin/gostring
- plugin/grpc
- plugin/marshalto
- plugin/oneofcheck
- plugin/populate
- plugin/size
- plugin/stringer
- plugin/testgen
- plugin/union
- plugin/unmarshal
- proto
- protoc-gen-gogo/descriptor
- protoc-gen-gogo/generator
- protoc-gen-gogo/plugin
- sortkeys
- vanity
- vanity/command
- name: github.com/golang/glog
version: 44145f04b68cf362d9c4df2182967c2275eaefed
- name: github.com/golang/protobuf
version: b982704f8bb716bb608144408cff30e15fbde841
subpackages:
- proto
- name: github.com/google/cadvisor
version: 4dbefc9b671b81257973a33211fb12370c1a526e
subpackages:
- api
- cache/memory
- collector
- container
- container/common
- container/docker
- container/libcontainer
- container/raw
- container/rkt
- container/systemd
- devicemapper
- events
- fs
- healthz
- http
- http/mux
- info/v1
- info/v1/test
- info/v2
- machine
- manager
- manager/watcher
- manager/watcher/raw
- manager/watcher/rkt
- metrics
- pages
- pages/static
- storage
- summary
- utils
- utils/cloudinfo
- utils/cpuload
- utils/cpuload/netlink
- utils/docker
- utils/oomparser
- utils/sysfs
- utils/sysinfo
- utils/tail
- validate
- version
- name: github.com/google/gofuzz
version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5
- name: github.com/jonboulle/clockwork
version: 3f831b65b61282ba6bece21b91beea2edc4c887a
- name: github.com/juju/ratelimit
version: 77ed1c8a01217656d2080ad51981f6e99adaa177
- name: github.com/klauspost/cpuid
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0
- name: github.com/matttproud/golang_protobuf_extensions
version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
subpackages:
- pbutil
- name: github.com/opencontainers/runc
version: 7ca2aa4873aea7cb4265b1726acb24b90d8726c6
subpackages:
- libcontainer
- libcontainer/apparmor
- libcontainer/cgroups
- libcontainer/cgroups/fs
- libcontainer/cgroups/systemd
- libcontainer/configs
- libcontainer/configs/validate
- libcontainer/criurpc
- libcontainer/label
- libcontainer/seccomp
- libcontainer/selinux
- libcontainer/stacktrace
- libcontainer/system
- libcontainer/user
- libcontainer/utils
- name: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
- difflib
- name: github.com/prometheus/client_golang
version: 3b78d7a77f51ccbc364d4bc170920153022cfd08
subpackages:
- prometheus
- name: github.com/prometheus/client_model
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
subpackages:
- go
- name: github.com/prometheus/common
version: a6ab08426bb262e2d190097751f5cfd1cfdfd17d
subpackages:
- expfmt
- internal/bitbucket.org/ww/goautoneg
- model
- name: github.com/prometheus/procfs
version: 490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d
- name: github.com/smartystreets/goconvey
version: d4c757aa9afd1e2fc1832aaab209b5794eb336e1
subpackages:
- convey
- convey/reporting
- convey/gotest
- name: github.com/spf13/pflag
version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5
- name: github.com/stretchr/objx
version: 1a9d0bb9f541897e62256577b352fdbc1fb4fd94
- name: github.com/stretchr/testify
version: f390dcf405f7b83c997eac1b06768bb9f44dec18
subpackages:
- mock
- assert
- name: github.com/ugorji/go
version: f4485b318aadd133842532f841dc205a8e339d74
subpackages:
- codec
- codec/codecgen
- name: github.com/vektra/errors
version: c64d83aba85aa4392895aadeefabbd24e89f3580
- name: golang.org/x/net
version: 62685c2d7ca23c807425dca88b11a3e2323dab41
subpackages:
- context
- context/ctxhttp
- html
- html/atom
- http2
- http2/hpack
- internal/timeseries
- proxy
- trace
- websocket
- name: golang.org/x/oauth2
version: b5adcc2dcdf009d0391547edc6ecbaff889f5bb9
subpackages:
- google
- internal
- jws
- jwt
- name: google.golang.org/appengine
version: 12d5545dc1cfa6047a286d5e853841b6471f4c19
subpackages:
- urlfetch
- internal
- internal/urlfetch
- internal/app_identity
- internal/modules
- internal/base
- internal/datastore
- internal/log
- internal/remote_api
- name: google.golang.org/cloud
version: eb47ba841d53d93506cfbfbc03927daf9cc48f88
subpackages:
- compute/metadata
- internal
- name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/yaml.v2
version: a83829b6f1293c91addabc89d0571c246397bbf4
- name: k8s.io/kubernetes
version: 283137936a498aed572ee22af6774b6fb6e9fd94
subpackages:
- pkg/api
- pkg/client/unversioned
- pkg/api/meta
- pkg/api/meta/metatypes
- pkg/api/resource
- pkg/api/unversioned
- pkg/auth/user
- pkg/conversion
- pkg/fields
- pkg/labels
- pkg/runtime
- pkg/runtime/serializer
- pkg/types
- pkg/util
- pkg/util/intstr
- pkg/util/rand
- pkg/util/sets
- pkg/api/errors
- pkg/api/install
- pkg/apimachinery/registered
- pkg/apis/apps
- pkg/apis/apps/install
- pkg/apis/authentication.k8s.io/install
- pkg/apis/authorization/install
- pkg/apis/autoscaling
- pkg/apis/autoscaling/install
- pkg/apis/batch
- pkg/apis/batch/install
- pkg/apis/batch/v2alpha1
- pkg/apis/componentconfig/install
- pkg/apis/extensions
- pkg/apis/extensions/install
- pkg/apis/policy
- pkg/apis/policy/install
- pkg/apis/rbac
- pkg/apis/rbac/install
- pkg/client/restclient
- pkg/client/typed/discovery
- pkg/util/net
- pkg/util/wait
- pkg/version
- pkg/watch
- plugin/pkg/client/auth
- pkg/util/errors
- third_party/forked/reflect
- pkg/util/validation
- pkg/conversion/queryparams
- pkg/util/json
- pkg/runtime/serializer/json
- pkg/runtime/serializer/protobuf
- pkg/runtime/serializer/recognizer
- pkg/runtime/serializer/versioning
- pkg/util/validation/field
- pkg/api/v1
- pkg/apimachinery
- pkg/watch/versioned
- pkg/apis/apps/v1alpha1
- pkg/apis/authentication.k8s.io
- pkg/apis/authentication.k8s.io/v1beta1
- pkg/apis/authorization
- pkg/apis/authorization/v1beta1
- pkg/apis/autoscaling/v1
- pkg/apis/batch/v1
- pkg/apis/componentconfig
- pkg/apis/componentconfig/v1alpha1
- pkg/apis/extensions/v1beta1
- pkg/apis/policy/v1alpha1
- pkg/apis/rbac/v1alpha1
- pkg/api/validation
- pkg/client/metrics
- pkg/client/transport
- pkg/client/unversioned/clientcmd/api
- pkg/runtime/serializer/streaming
- pkg/util/crypto
- pkg/util/flowcontrol
- pkg/util/runtime
- plugin/pkg/client/auth/gcp
- plugin/pkg/client/auth/oidc
- pkg/util/framer
- pkg/util/yaml
- pkg/util/parsers
- pkg/kubelet/qos
- pkg/master/ports
- pkg/api/endpoints
- pkg/api/pod
- pkg/api/service
- pkg/api/unversioned/validation
- pkg/api/util
- pkg/capabilities
- pkg/util/integer
- pkg/kubelet/qos/util
- pkg/util/hash
- pkg/util/net/sets
testImports:
- name: github.com/gopherjs/gopherjs
version: 72e303cb5f235b23471872477b57e573dc1c71d0
subpackages:
- js
- name: github.com/jtolds/gls
version: 8ddce2a84170772b95dd5d576c48d517b22cac63
- name: github.com/smartystreets/assertions
version: 2063fd1cc7c975db70502811a34b06ad034ccdf2
subpackages:
- internal/go-render/render
- internal/oglematchers

20
glide.yaml Normal file
View file

@ -0,0 +1,20 @@
package: github.com/kubernetes-incubator/node-feature-discovery
import:
- package: github.com/klauspost/cpuid
version: v1.0
- package: github.com/docopt/docopt.go
version: ^0.6.2
- package: k8s.io/kubernetes
version: v1.3.0
subpackages:
- pkg/api
- pkg/client/unversioned
- package: github.com/smartystreets/goconvey
version: 1.6.2
subpackages:
- convey
- package: github.com/stretchr/testify
version: v1.1.3
subpackages:
- mock
- package: github.com/vektra/errors

12
label-nodes.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Get the node count in the Kubernetes cluster
NumNodes=$(kubectl get nodes | grep -i ready | wc -l)
# We set the .spec.completions and .spec.parallelism to the node count
# We request a specific hostPort in the job spec to limit the number of pods
# that run on a node to one. As a result, one pod runs on each node in parallel
# We set the POD_NAME and POD_NAMESPACE environemnt variables to the pod name
# and pod namespace. These enivornment variables are used by the feature
# discovery software to get the Kubernetes pod and node object.
sed -e "s/COMPLETION_COUNT/$NumNodes/" -e "s/PARALLELISM_COUNT/$NumNodes/" node-feature-discovery-job.json.template > node-feature-discovery-job.json
kubectl create -f node-feature-discovery-job.json

233
main.go Normal file
View file

@ -0,0 +1,233 @@
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/docopt/docopt-go"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
)
const (
// ProgramName is the canonical name of this discovery program.
ProgramName = "node-feature-discovery"
// Namespace is the prefix for all published labels.
Namespace = "node.alpha.intel.com"
// PodNameEnv is the environment variable that contains this pod's name.
PodNameEnv = "POD_NAME"
// PodNamespaceEnv is the environment variable that contains this pod's
// namespace.
PodNamespaceEnv = "POD_NAMESPACE"
)
var (
version = "" // Must not be const, set using ldflags at build time
prefix = fmt.Sprintf("%s/%s", Namespace, version)
)
// Labels are a Kubernetes representation of discovered features.
type Labels map[string]string
// APIHelpers represents a set of API helpers for Kubernetes
type APIHelpers interface {
// GetClient returns a client
GetClient() (*client.Client, error)
// GetNode returns the Kubernetes node on which this container is running.
GetNode(*client.Client) (*api.Node, error)
// addLabels modifies the supplied node's labels collection.
// In order to publish the labels, the node must be subsequently updated via the
// API server using the client library.
AddLabels(*api.Node, Labels)
// UpdateNode updates the node via the API server using a client.
UpdateNode(*client.Client, *api.Node) error
}
func main() {
// Assert that the version is known
if version == "" {
log.Fatalf("main.version not set! Set -ldflags \"-X main.version `git describe --tags --dirty --always`\" during build or run.")
}
usage := fmt.Sprintf(`%s.
Usage:
%s [--no-publish --sources=<sources>]
%s -h | --help
%s --version
Options:
-h --help Show this screen.
--version Output version and exit.
--sources=<sources> Comma separated list of feature sources.
[Default: cpuid,rdt,pstate]
--no-publish Do not publish discovered features to the cluster-local
Kubernetes API server.`,
ProgramName,
ProgramName,
ProgramName,
ProgramName,
)
arguments, _ := docopt.Parse(usage, nil, true,
fmt.Sprintf("%s %s", ProgramName, version), false)
// Parse argument values as usable types.
noPublish := arguments["--no-publish"].(bool)
sourcesArg := strings.Split(arguments["--sources"].(string), ",")
enabledSources := map[string]struct{}{}
for _, s := range sourcesArg {
enabledSources[strings.TrimSpace(s)] = struct{}{}
}
// Configure feature sources.
allSources := []FeatureSource{
cpuidSource{},
rdtSource{},
pstateSource{},
}
sources := []FeatureSource{}
for _, s := range allSources {
if _, enabled := enabledSources[s.Name()]; enabled {
sources = append(sources, s)
}
}
labels := Labels{}
// Add the version of this discovery code as a node label
versionLabel := fmt.Sprintf("%s/%s.version", Namespace, ProgramName)
labels[versionLabel] = version
// Log version label.
log.Printf("%s = %s", versionLabel, version)
// Do feature discovery from all configured sources.
for _, source := range sources {
labelsFromSource, err := getFeatureLabels(source)
if err != nil {
log.Fatalf("discovery failed for source [%s]: %s", source.Name(), err.Error())
}
for name, value := range labelsFromSource {
labels[name] = value
// Log discovered feature.
log.Printf("%s = %s", name, value)
}
}
// Update the node with the node labels, unless disabled via flags.
if !noPublish {
helper := APIHelpers(k8sHelpers{})
err := advertiseFeatureLabels(helper, labels)
if err != nil {
log.Fatalf("failed to advertise labels: %s", err.Error())
}
}
}
// getFeatureLabels returns node labels for features discovered by the
// supplied source.
func getFeatureLabels(source FeatureSource) (Labels, error) {
labels := Labels{}
features, err := source.Discover()
if err != nil {
return nil, err
}
for _, f := range features {
labels[fmt.Sprintf("%s-%s-%s", prefix, source.Name(), f)] = "true"
}
return labels, nil
}
// advertiseFeatureLabels advertises the feature labels to a Kubernetes node
// via the API server.
func advertiseFeatureLabels(helper APIHelpers, labels Labels) error {
// Set up K8S client.
cli, err := helper.GetClient()
if err != nil {
log.Printf("can't get kubernetes client: %s", err.Error())
return err
}
// Get the current node.
node, err := helper.GetNode(cli)
if err != nil {
log.Printf("failed to get node: %s", err.Error())
return err
}
// Add labels to the node object.
helper.AddLabels(node, labels)
// Send the updated node to the apiserver.
err = helper.UpdateNode(cli, node)
if err != nil {
log.Printf("can't update node: %s", err.Error())
return err
}
return nil
}
// Implements main.APIHelpers
type k8sHelpers struct{}
func (h k8sHelpers) GetClient() (*client.Client, error) {
// Set up K8S client.
cli, err := client.NewInCluster()
if err != nil {
return nil, err
}
return cli, nil
}
func (h k8sHelpers) GetNode(cli *client.Client) (*api.Node, error) {
// Get the pod name and pod namespace from the env variables
podName := os.Getenv(PodNameEnv)
podns := os.Getenv(PodNamespaceEnv)
log.Printf("%s: %s", PodNameEnv, podName)
log.Printf("%s: %s", PodNamespaceEnv, podns)
// Get the pod object using the pod name and pod namespace
pod, err := cli.Pods(podns).Get(podName)
if err != nil {
log.Printf("can't get pods: %s", err.Error())
return nil, err
}
// Get the node object using the pod name and pod namespace
node, err := cli.Nodes().Get(pod.Spec.NodeName)
if err != nil {
log.Printf("can't get node: %s", err.Error())
return nil, err
}
return node, nil
}
func (h k8sHelpers) AddLabels(n *api.Node, labels Labels) {
for k, v := range labels {
n.Labels[k] = v
}
}
func (h k8sHelpers) UpdateNode(c *client.Client, n *api.Node) error {
// Send the updated node to the apiserver.
_, err := c.Nodes().Update(n)
if err != nil {
return err
}
return nil
}

102
main_test.go Normal file
View file

@ -0,0 +1,102 @@
package main
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/vektra/errors"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
)
func TestDiscoveryWithMockSources(t *testing.T) {
Convey("When I discover features from fake source and update the node using fake client", t, func() {
mockFeatureSource := new(MockFeatureSource)
fakeFeatureSourceName := string("testSource")
fakeFeatures := []string{"testfeature1", "testfeature2", "testfeature3"}
fakeFeatureLabels := Labels{}
for _, f := range fakeFeatures {
fakeFeatureLabels[fmt.Sprintf("%s-testSource-%s", prefix, f)] = "true"
}
fakeFeatureSource := FeatureSource(mockFeatureSource)
Convey("When I successfully get the labels from the mock source", func() {
mockFeatureSource.On("Name").Return(fakeFeatureSourceName)
mockFeatureSource.On("Discover").Return(fakeFeatures, nil)
returnedLabels, err := getFeatureLabels(fakeFeatureSource)
Convey("Proper label is returned", func() {
So(returnedLabels, ShouldResemble, fakeFeatureLabels)
})
Convey("Error is nil", func() {
So(err, ShouldBeNil)
})
})
Convey("When I fail to get the labels from the mock source", func() {
expectedError := errors.New("fake error")
mockFeatureSource.On("Discover").Return(nil, expectedError)
returnedLabels, err := getFeatureLabels(fakeFeatureSource)
Convey("No label is returned", func() {
So(returnedLabels, ShouldBeNil)
})
Convey("Error is produced", func() {
So(err, ShouldEqual, expectedError)
})
})
mockAPIHelper := new(MockAPIHelpers)
testHelper := APIHelpers(mockAPIHelper)
var mockClient *client.Client
var mockNode *api.Node
Convey("When I successfully advertise feature labels to a node", func() {
mockAPIHelper.On("GetClient").Return(mockClient, nil)
mockAPIHelper.On("GetNode", mockClient).Return(mockNode, nil).Once()
mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once()
mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(nil).Once()
err := advertiseFeatureLabels(testHelper, fakeFeatureLabels)
Convey("Error is nil", func() {
So(err, ShouldBeNil)
})
})
Convey("When I fail to get a mock client while advertising feature labels", func() {
expectedError := errors.New("fake error")
mockAPIHelper.On("GetClient").Return(nil, expectedError)
err := advertiseFeatureLabels(testHelper, fakeFeatureLabels)
Convey("Error is produced", func() {
So(err, ShouldEqual, expectedError)
})
})
Convey("When I fail to get a mock node while advertising feature labels", func() {
expectedError := errors.New("fake error")
mockAPIHelper.On("GetClient").Return(mockClient, nil)
mockAPIHelper.On("GetNode", mockClient).Return(nil, expectedError).Once()
err := advertiseFeatureLabels(testHelper, fakeFeatureLabels)
Convey("Error is produced", func() {
So(err, ShouldEqual, expectedError)
})
})
Convey("When I fail to update a mock node while advertising feature labels", func() {
expectedError := errors.New("fake error")
mockAPIHelper.On("GetClient").Return(mockClient, nil)
mockAPIHelper.On("GetNode", mockClient).Return(mockNode, nil).Once()
mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once()
mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(expectedError).Once()
err := advertiseFeatureLabels(testHelper, fakeFeatureLabels)
Convey("Error is produced", func() {
So(err, ShouldEqual, expectedError)
})
})
})
}

80
mockapihelpers.go Normal file
View file

@ -0,0 +1,80 @@
package main
import (
"github.com/stretchr/testify/mock"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
)
type MockAPIHelpers struct {
mock.Mock
}
// GetClient provides a mock function with no input arguments and
// *client.Client and error as return value
func (_m *MockAPIHelpers) GetClient() (*client.Client, error) {
ret := _m.Called()
var r0 *client.Client
if rf, ok := ret.Get(0).(func() *client.Client); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*client.Client)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetNode provides a mock function with *client.Client as input argument and
// *api.Node and error as return values
func (_m *MockAPIHelpers) GetNode(_a0 *client.Client) (*api.Node, error) {
ret := _m.Called(_a0)
var r0 *api.Node
if rf, ok := ret.Get(0).(func(*client.Client) *api.Node); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*api.Node)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*client.Client) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AddLabels provides a mock function with *api.Node and main.Labels as the input arguments and
// no return value
func (_m *MockAPIHelpers) AddLabels(_a0 *api.Node, _a1 Labels) {
_m.Called(_a0, _a1)
}
// UpdateNode provides a mock function with *client.Client and *api.Node as the input arguments and
// error as the return value
func (_m *MockAPIHelpers) UpdateNode(_a0 *client.Client, _a1 *api.Node) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(*client.Client, *api.Node) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}

46
mockfeaturesource.go Normal file
View file

@ -0,0 +1,46 @@
package main
import "github.com/stretchr/testify/mock"
type MockFeatureSource struct {
mock.Mock
}
// Name provides a mock function with no input arguments
// and string as return value
func (_m *MockFeatureSource) Name() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Discover provides a mock function with no input arguments
// and []string and error as the return values
func (_m *MockFeatureSource) Discover() ([]string, error) {
ret := _m.Called()
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View file

@ -0,0 +1,54 @@
{
"apiVersion": "extensions/v1beta1",
"kind": "Job",
"metadata": {
"labels": {
"app": "node-feature-discovery"
},
"name": "node-feature-discovery"
},
"spec": {
"completions": COMPLETION_COUNT,
"parallelism": PARALLELISM_COUNT,
"template": {
"metadata": {
"labels": {
"app": "node-feature-discovery"
}
},
"spec": {
"containers": [
{
"env": [
{
"name": "POD_NAME",
"valueFrom": {
"fieldRef": {
"fieldPath": "metadata.name"
}
}
},
{
"name": "POD_NAMESPACE",
"valueFrom": {
"fieldRef": {
"fieldPath": "metadata.namespace"
}
}
}
],
"image": "kubernetesincubator/node-feature-discovery",
"name": "node-feature-discovery",
"ports": [
{
"containerPort": 2233,
"hostPort": 7156
}
]
}
],
"restartPolicy": "Never"
}
}
}
}

14
rdt-discovery/Makefile Normal file
View file

@ -0,0 +1,14 @@
CC=gcc
LIBDIR=/usr/local/lib
INCDIR=/go/src/github.com/kubernetes-incubator/node-feature-discovery/intel-cmt-cat/lib/
LDFLAGS=-L$(LIBDIR)
LDLIBS=-lpqos
CFLAGS=-I$(INCDIR)
default:
$(MAKE) all
all:
$(CC) $(CFLAGS) -o mon-discovery monitoring-discovery.c $(LDFLAGS) $(LDLIBS)
$(CC) $(CFLAGS) -o l3-alloc-discovery l3-allocation-discovery.c $(LDFLAGS) $(LDLIBS)
$(CC) $(CFLAGS) -o l2-alloc-discovery l2-allocation-discovery.c $(LDFLAGS) $(LDLIBS)

View file

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include "machine.h"
int main(int argc, char *argv[]) {
int ret, det=1;
struct cpuid_out res;
// Logic below from https://github.com/01org/intel-cmt-cat/blob/master/lib/host_cap.c
lcpuid(0x7, 0x0, &res);
if (!(res.ebx & (1 << 15))) {
det = 0;
printf("NOT DETECTED");
}
else {
lcpuid(0x10, 0x0, &res);
if (!(res.ebx & (1 << 2))) {
det = 0;
printf("NOT DETECTED");
}
}
if (det)
printf("DETECTED");
return 0;
}

View file

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include "machine.h"
int main(int argc, char *argv[]) {
int ret, det=1;
struct cpuid_out res;
// Logic below from https://github.com/01org/intel-cmt-cat/blob/master/lib/host_cap.c
// TODO(balajismaniam): Implement L3 CAT detection using brand string and MSR probing if
// not detected using cpuid
lcpuid(0x7, 0x0, &res);
if (!(res.ebx & (1 << 15))) {
det = 0;
printf("NOT DETECTED");
}
else {
lcpuid(0x10, 0x0, &res);
if (!(res.ebx & (1 << 1))) {
det = 0;
printf("NOT DETECTED");
}
}
if (det)
printf("DETECTED");
return 0;
}

View file

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include "machine.h"
int main(int argc, char *argv[]) {
int ret, det=1;
struct cpuid_out res;
// Logic below from https://github.com/01org/intel-cmt-cat/blob/master/lib/host_cap.c
lcpuid(0x7, 0x0, &res);
if (!(res.ebx & (1 << 12))) {
det = 0;
printf("NOT DETECTED");
}
else {
lcpuid(0xf, 0x0, &res);
if (!(res.edx & (1 << 1))) {
det=0;
printf("NOT DETECTED");
}
}
if (det)
printf("DETECTED");
return 0;
}

104
sources.go Normal file
View file

@ -0,0 +1,104 @@
package main
import (
"fmt"
"io/ioutil"
"os/exec"
"path"
"github.com/klauspost/cpuid"
)
// FeatureSource represents a source of discovered node features.
type FeatureSource interface {
// Returns a friendly name for this source of node features.
Name() string
// Returns discovered features for this node.
Discover() ([]string, error)
}
const (
// DETECTED is compared with stdout for RDT detection helper programs.
DETECTED = "DETECTED"
// RDTBin is the path to RDT detection helpers.
RDTBin = "/go/src/github.com/kubernetes-incubator/node-feature-discovery/rdt-discovery"
)
////////////////////////////////////////////////////////////////////////////////
// CPUID Source
// Implements main.FeatureSource.
type cpuidSource struct{}
func (s cpuidSource) Name() string { return "cpuid" }
func (s cpuidSource) Discover() ([]string, error) {
// Get the cpu features as strings
return cpuid.CPU.Features.Strings(), nil
}
////////////////////////////////////////////////////////////////////////////////
// RDT (Intel Resource Director Technology) Source
// Implements main.FeatureSource.
type rdtSource struct{}
func (s rdtSource) Name() string { return "rdt" }
// Returns feature names for CMT, MBM and CAT if suppported.
func (s rdtSource) Discover() ([]string, error) {
features := []string{}
out, err := exec.Command("bash", "-c", path.Join(RDTBin, "mon-discovery")).Output()
if err != nil {
return nil, fmt.Errorf("can't detect support for RDT monitoring: %s", err.Error())
}
if string(out[:]) == DETECTED {
// RDT monitoring detected.
features = append(features, "RDTMON")
}
out, err = exec.Command("bash", "-c", path.Join(RDTBin, "l3-alloc-discovery")).Output()
if err != nil {
return nil, fmt.Errorf("can't detect support for RDT L3 allocation: %s", err.Error())
}
if string(out[:]) == DETECTED {
// RDT L3 cache allocation detected.
features = append(features, "RDTL3CA")
}
out, err = exec.Command("bash", "-c", path.Join(RDTBin, "l2-alloc-discovery")).Output()
if err != nil {
return nil, fmt.Errorf("can't detect support for RDT L2 allocation: %s", err.Error())
}
if string(out[:]) == DETECTED {
// RDT L2 cache allocation detected.
features = append(features, "RDTL2CA")
}
return features, nil
}
////////////////////////////////////////////////////////////////////////////////
// PState Source
// Implements main.FeatureSource.
type pstateSource struct{}
func (s pstateSource) Name() string { return "pstate" }
func (s pstateSource) Discover() ([]string, error) {
features := []string{}
// Only looking for turbo boost for now...
bytes, err := ioutil.ReadFile("/sys/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())
}
if bytes[0] == byte('0') {
// Turbo boost is enabled.
features = append(features, "turbo")
}
return features, nil
}