1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-04-16 09:16:31 +00:00

Merge pull request #326 from marquiz/devel/prune

nfd-master: implement --prune flag
This commit is contained in:
Kubernetes Prow Robot 2020-09-07 04:51:42 -07:00 committed by GitHub
commit 9df73b8ace
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 15 deletions

View file

@ -50,18 +50,20 @@ Command line flags of nfd-master:
```
$ docker run --rm <NFD_CONTAINER_IMAGE> nfd-master --help
...
nfd-master.
Usage:
nfd-master [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
Usage:
nfd-master [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
[--kubeconfig=<path>]
nfd-master -h | --help
nfd-master --version
Options:
-h --help Show this screen.
--version Output version and exit.
--prune Prune all NFD related attributes from all nodes
of the cluster and exit.
--kubeconfig=<path> Kubeconfig to use [Default: ]
--port=<port> Port on which to listen for connections.
[Default: 8080]
--ca-file=<path> Root certificate for verifying connections

View file

@ -63,15 +63,20 @@ func argsParse(argv []string) (master.Args, error) {
usage := fmt.Sprintf(`%s.
Usage:
%s [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
%s [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
[--kubeconfig=<path>]
%s -h | --help
%s --version
Options:
-h --help Show this screen.
--version Output version and exit.
--prune Prune all NFD related attributes from all nodes
of the cluster and exit.
--kubeconfig=<path> Kubeconfig to use [Default: ]
of the cluster and exit.
--port=<port> Port on which to listen for connections.
[Default: 8080]
--ca-file=<path> Root certificate for verifying connections
@ -119,6 +124,8 @@ func argsParse(argv []string) (master.Args, error) {
args.VerifyNodeName = arguments["--verify-node-name"].(bool)
args.ExtraLabelNs = strings.Split(arguments["--extra-label-ns"].(string), ",")
args.ResourceLabels = strings.Split(arguments["--resource-labels"].(string), ",")
args.Prune = arguments["--prune"].(bool)
args.Kubeconfig = arguments["--kubeconfig"].(string)
return args, nil
}

View file

@ -29,6 +29,9 @@ type APIHelpers interface {
// GetNode returns the Kubernetes node on which this container is running.
GetNode(*k8sclient.Clientset, string) (*api.Node, error)
// GetNodes returns all the nodes in the cluster
GetNodes(*k8sclient.Clientset) (*api.NodeList, error)
// UpdateNode updates the node via the API server using a client.
UpdateNode(*k8sclient.Clientset, *api.Node) error

View file

@ -24,18 +24,28 @@ import (
"k8s.io/apimachinery/pkg/types"
k8sclient "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
// Implements APIHelpers
type K8sHelpers struct {
Kubeconfig string
}
func (h K8sHelpers) GetClient() (*k8sclient.Clientset, error) {
// Set up an in-cluster K8S client.
config, err := restclient.InClusterConfig()
var config *restclient.Config
var err error
if h.Kubeconfig == "" {
config, err = restclient.InClusterConfig()
} else {
config, err = clientcmd.BuildConfigFromFlags("", h.Kubeconfig)
}
if err != nil {
return nil, err
}
clientset, err := k8sclient.NewForConfig(config)
if err != nil {
return nil, err
@ -53,6 +63,10 @@ func (h K8sHelpers) GetNode(cli *k8sclient.Clientset, nodeName string) (*api.Nod
return node, nil
}
func (h K8sHelpers) GetNodes(cli *k8sclient.Clientset) (*api.NodeList, error) {
return cli.CoreV1().Nodes().List(meta_v1.ListOptions{})
}
func (h K8sHelpers) UpdateNode(c *k8sclient.Clientset, n *api.Node) error {
// Send the updated node to the apiserver.
_, err := c.CoreV1().Nodes().Update(n)

View file

@ -62,6 +62,29 @@ func (_m *MockAPIHelpers) GetNode(_a0 *kubernetes.Clientset, _a1 string) (*v1.No
return r0, r1
}
// GetNodes provides a mock function with given fields: _a0
func (_m *MockAPIHelpers) GetNodes(_a0 *kubernetes.Clientset) (*v1.NodeList, error) {
ret := _m.Called(_a0)
var r0 *v1.NodeList
if rf, ok := ret.Get(0).(func(*kubernetes.Clientset) *v1.NodeList); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1.NodeList)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kubernetes.Clientset) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PatchStatus provides a mock function with given fields: _a0, _a1, _a2
func (_m *MockAPIHelpers) PatchStatus(_a0 *kubernetes.Clientset, _a1 string, _a2 interface{}) error {
ret := _m.Called(_a0, _a1, _a2)

View file

@ -70,9 +70,11 @@ type Args struct {
CertFile string
ExtraLabelNs []string
KeyFile string
Kubeconfig string
LabelWhiteList *regexp.Regexp
NoPublish bool
Port int
Prune bool
VerifyNodeName bool
ResourceLabels []string
}
@ -87,6 +89,7 @@ type nfdMaster struct {
args Args
server *grpc.Server
ready chan bool
apihelper apihelper.APIHelpers
}
// statusOp is a json marshaling helper used for patching node status
@ -121,6 +124,9 @@ func NewNfdMaster(args Args) (NfdMaster, error) {
}
}
// Initialize Kubernetes API helpers
nfd.apihelper = apihelper.K8sHelpers{Kubeconfig: args.Kubeconfig}
return nfd, nil
}
@ -130,11 +136,12 @@ func (m *nfdMaster) Run() error {
stdoutLogger.Printf("Node Feature Discovery Master %s", version.Get())
stdoutLogger.Printf("NodeName: '%s'", nodeName)
// Initialize Kubernetes API helpers
helper := apihelper.APIHelpers(apihelper.K8sHelpers{})
if m.args.Prune {
return m.prune()
}
if !m.args.NoPublish {
err := updateMasterNode(helper)
err := updateMasterNode(m.apihelper)
if err != nil {
return fmt.Errorf("failed to update master node: %v", err)
}
@ -176,7 +183,7 @@ func (m *nfdMaster) Run() error {
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
}
m.server = grpc.NewServer(serverOpts...)
pb.RegisterLabelerServer(m.server, &labelerServer{args: m.args, apiHelper: helper})
pb.RegisterLabelerServer(m.server, &labelerServer{args: m.args, apiHelper: m.apihelper})
stdoutLogger.Printf("gRPC server serving on port: %d", m.args.Port)
return m.server.Serve(lis)
}
@ -201,6 +208,46 @@ func (m *nfdMaster) WaitForReady(timeout time.Duration) bool {
return false
}
// Prune erases all NFD related properties from the node objects of the cluster.
func (m *nfdMaster) prune() error {
cli, err := m.apihelper.GetClient()
if err != nil {
return err
}
nodes, err := m.apihelper.GetNodes(cli)
if err != nil {
return err
}
for _, node := range nodes.Items {
stdoutLogger.Printf("pruning node %q...", node.Name)
// Prune labels and extended resources
err := updateNodeFeatures(m.apihelper, node.Name, Labels{}, Annotations{}, ExtendedResources{})
if err != nil {
return fmt.Errorf("failed to prune labels from node %q: %v", node.Name, err)
}
// Prune annotations
node, err := m.apihelper.GetNode(cli, node.Name)
if err != nil {
return err
}
for a := range node.Annotations {
if strings.HasPrefix(a, AnnotationNs) {
delete(node.Annotations, a)
}
}
err = m.apihelper.UpdateNode(cli, node)
if err != nil {
return fmt.Errorf("failed to prune annotations from node %q: %v", node.Name, err)
}
}
return nil
}
// Advertise NFD master information
func updateMasterNode(helper apihelper.APIHelpers) error {
cli, err := helper.GetClient()
@ -332,8 +379,9 @@ func (s *labelerServer) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*p
return &pb.SetLabelsReply{}, nil
}
// advertiseFeatureLabels advertises the feature labels to a Kubernetes node
// via the API server.
// updateNodeFeatures ensures the Kubernetes node object is up to date,
// creating new labels and extended resources where necessary and removing
// outdated ones. Also updates the corresponding annotations.
func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Labels, annotations Annotations, extendedResources ExtendedResources) error {
cli, err := helper.GetClient()
if err != nil {