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:
commit
9df73b8ace
6 changed files with 112 additions and 15 deletions
10
README.md
10
README.md
|
@ -50,18 +50,20 @@ Command line flags of nfd-master:
|
||||||
```
|
```
|
||||||
$ docker run --rm <NFD_CONTAINER_IMAGE> nfd-master --help
|
$ docker run --rm <NFD_CONTAINER_IMAGE> nfd-master --help
|
||||||
...
|
...
|
||||||
nfd-master.
|
Usage:
|
||||||
|
nfd-master [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
|
||||||
Usage:
|
|
||||||
nfd-master [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
|
|
||||||
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
||||||
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
||||||
|
[--kubeconfig=<path>]
|
||||||
nfd-master -h | --help
|
nfd-master -h | --help
|
||||||
nfd-master --version
|
nfd-master --version
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
--version Output version and exit.
|
--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.
|
--port=<port> Port on which to listen for connections.
|
||||||
[Default: 8080]
|
[Default: 8080]
|
||||||
--ca-file=<path> Root certificate for verifying connections
|
--ca-file=<path> Root certificate for verifying connections
|
||||||
|
|
|
@ -63,15 +63,20 @@ func argsParse(argv []string) (master.Args, error) {
|
||||||
usage := fmt.Sprintf(`%s.
|
usage := fmt.Sprintf(`%s.
|
||||||
|
|
||||||
Usage:
|
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>]
|
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
||||||
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
||||||
|
[--kubeconfig=<path>]
|
||||||
%s -h | --help
|
%s -h | --help
|
||||||
%s --version
|
%s --version
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
--version Output version and exit.
|
--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.
|
--port=<port> Port on which to listen for connections.
|
||||||
[Default: 8080]
|
[Default: 8080]
|
||||||
--ca-file=<path> Root certificate for verifying connections
|
--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.VerifyNodeName = arguments["--verify-node-name"].(bool)
|
||||||
args.ExtraLabelNs = strings.Split(arguments["--extra-label-ns"].(string), ",")
|
args.ExtraLabelNs = strings.Split(arguments["--extra-label-ns"].(string), ",")
|
||||||
args.ResourceLabels = strings.Split(arguments["--resource-labels"].(string), ",")
|
args.ResourceLabels = strings.Split(arguments["--resource-labels"].(string), ",")
|
||||||
|
args.Prune = arguments["--prune"].(bool)
|
||||||
|
args.Kubeconfig = arguments["--kubeconfig"].(string)
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ type APIHelpers interface {
|
||||||
// GetNode returns the Kubernetes node on which this container is running.
|
// GetNode returns the Kubernetes node on which this container is running.
|
||||||
GetNode(*k8sclient.Clientset, string) (*api.Node, error)
|
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 updates the node via the API server using a client.
|
||||||
UpdateNode(*k8sclient.Clientset, *api.Node) error
|
UpdateNode(*k8sclient.Clientset, *api.Node) error
|
||||||
|
|
||||||
|
|
|
@ -24,18 +24,28 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
k8sclient "k8s.io/client-go/kubernetes"
|
k8sclient "k8s.io/client-go/kubernetes"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implements APIHelpers
|
// Implements APIHelpers
|
||||||
type K8sHelpers struct {
|
type K8sHelpers struct {
|
||||||
|
Kubeconfig string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h K8sHelpers) GetClient() (*k8sclient.Clientset, error) {
|
func (h K8sHelpers) GetClient() (*k8sclient.Clientset, error) {
|
||||||
// Set up an in-cluster K8S client.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clientset, err := k8sclient.NewForConfig(config)
|
clientset, err := k8sclient.NewForConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -53,6 +63,10 @@ func (h K8sHelpers) GetNode(cli *k8sclient.Clientset, nodeName string) (*api.Nod
|
||||||
return node, nil
|
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 {
|
func (h K8sHelpers) UpdateNode(c *k8sclient.Clientset, n *api.Node) error {
|
||||||
// Send the updated node to the apiserver.
|
// Send the updated node to the apiserver.
|
||||||
_, err := c.CoreV1().Nodes().Update(n)
|
_, err := c.CoreV1().Nodes().Update(n)
|
||||||
|
|
|
@ -62,6 +62,29 @@ func (_m *MockAPIHelpers) GetNode(_a0 *kubernetes.Clientset, _a1 string) (*v1.No
|
||||||
return r0, r1
|
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
|
// PatchStatus provides a mock function with given fields: _a0, _a1, _a2
|
||||||
func (_m *MockAPIHelpers) PatchStatus(_a0 *kubernetes.Clientset, _a1 string, _a2 interface{}) error {
|
func (_m *MockAPIHelpers) PatchStatus(_a0 *kubernetes.Clientset, _a1 string, _a2 interface{}) error {
|
||||||
ret := _m.Called(_a0, _a1, _a2)
|
ret := _m.Called(_a0, _a1, _a2)
|
||||||
|
|
|
@ -70,9 +70,11 @@ type Args struct {
|
||||||
CertFile string
|
CertFile string
|
||||||
ExtraLabelNs []string
|
ExtraLabelNs []string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
|
Kubeconfig string
|
||||||
LabelWhiteList *regexp.Regexp
|
LabelWhiteList *regexp.Regexp
|
||||||
NoPublish bool
|
NoPublish bool
|
||||||
Port int
|
Port int
|
||||||
|
Prune bool
|
||||||
VerifyNodeName bool
|
VerifyNodeName bool
|
||||||
ResourceLabels []string
|
ResourceLabels []string
|
||||||
}
|
}
|
||||||
|
@ -84,9 +86,10 @@ type NfdMaster interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type nfdMaster struct {
|
type nfdMaster struct {
|
||||||
args Args
|
args Args
|
||||||
server *grpc.Server
|
server *grpc.Server
|
||||||
ready chan bool
|
ready chan bool
|
||||||
|
apihelper apihelper.APIHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
// statusOp is a json marshaling helper used for patching node status
|
// 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
|
return nfd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,11 +136,12 @@ func (m *nfdMaster) Run() error {
|
||||||
stdoutLogger.Printf("Node Feature Discovery Master %s", version.Get())
|
stdoutLogger.Printf("Node Feature Discovery Master %s", version.Get())
|
||||||
stdoutLogger.Printf("NodeName: '%s'", nodeName)
|
stdoutLogger.Printf("NodeName: '%s'", nodeName)
|
||||||
|
|
||||||
// Initialize Kubernetes API helpers
|
if m.args.Prune {
|
||||||
helper := apihelper.APIHelpers(apihelper.K8sHelpers{})
|
return m.prune()
|
||||||
|
}
|
||||||
|
|
||||||
if !m.args.NoPublish {
|
if !m.args.NoPublish {
|
||||||
err := updateMasterNode(helper)
|
err := updateMasterNode(m.apihelper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update master node: %v", err)
|
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)))
|
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
||||||
}
|
}
|
||||||
m.server = grpc.NewServer(serverOpts...)
|
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)
|
stdoutLogger.Printf("gRPC server serving on port: %d", m.args.Port)
|
||||||
return m.server.Serve(lis)
|
return m.server.Serve(lis)
|
||||||
}
|
}
|
||||||
|
@ -201,6 +208,46 @@ func (m *nfdMaster) WaitForReady(timeout time.Duration) bool {
|
||||||
return false
|
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
|
// Advertise NFD master information
|
||||||
func updateMasterNode(helper apihelper.APIHelpers) error {
|
func updateMasterNode(helper apihelper.APIHelpers) error {
|
||||||
cli, err := helper.GetClient()
|
cli, err := helper.GetClient()
|
||||||
|
@ -332,8 +379,9 @@ func (s *labelerServer) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*p
|
||||||
return &pb.SetLabelsReply{}, nil
|
return &pb.SetLabelsReply{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// advertiseFeatureLabels advertises the feature labels to a Kubernetes node
|
// updateNodeFeatures ensures the Kubernetes node object is up to date,
|
||||||
// via the API server.
|
// 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 {
|
func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Labels, annotations Annotations, extendedResources ExtendedResources) error {
|
||||||
cli, err := helper.GetClient()
|
cli, err := helper.GetClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue