Repository: kubernetes-sigs/kwok
Pull request #322 (2023-02-23 09:02:18)

Bump requirements
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide/first-contribution.md#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/sig-testing/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

/kind cleanup

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note

```

#### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

<!--
This section can be blank if this pull request does not require a release note.

When adding links which point to resources within git repositories, like
KEPs or supporting documentation, please reference a specific commit and avoid
linking directly to the master branch. This ensures that links reference a
specific point in time, rather than a document that may change over time.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [KEP]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```



Patch:
diff --git a/hack/requirements.sh b/hack/requirements.sh
index 3c0cd959..8c5b418a 100755
--- a/hack/requirements.sh
+++ b/hack/requirements.sh
@@ -13,15 +13,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-KIND_VERSION=0.14.0
+KIND_VERSION=0.17.0
 
-KUBE_VERSION=1.24.2
+KUBE_VERSION=1.26.0
 
-BUILDX_VERSION=0.8.2
+BUILDX_VERSION=0.10.3
 
-COMPOSE_VERSION=2.10.1
+COMPOSE_VERSION=2.13.0
 
-NERDCTL_VERSION=1.0.0
+NERDCTL_VERSION=1.6.0
 
 function command_exist() {
   local command="${1}"
diff --git a/pkg/kwok/controllers/controller.go b/pkg/kwok/controllers/controller.go
index 530ceb5c..caabe62b 100644
--- a/pkg/kwok/controllers/controller.go
+++ b/pkg/kwok/controllers/controller.go
@@ -145,6 +145,7 @@ func NewController(conf Config) (*Controller, error) {
 		DisregardStatusWithLabelSelector:      conf.DisregardStatusWithLabelSelector,
 		Stages:                                conf.PodStages,
 		LockPodParallelism:                    16,
+		NodeHasFunc:                           nodes.Has, // just handle pods that are on nodes we have
 		NodeGetFunc:                           nodes.Get,
 		FuncMap:                               defaultFuncMap,
 		Recorder:                              recorder,
diff --git a/pkg/kwok/controllers/node_controller.go b/pkg/kwok/controllers/node_controller.go
index 4709f287..e613f423 100644
--- a/pkg/kwok/controllers/node_controller.go
+++ b/pkg/kwok/controllers/node_controller.go
@@ -22,6 +22,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
+	"sync"
 	"text/template"
 	"time"
 
@@ -42,7 +43,6 @@ import (
 	"sigs.k8s.io/kwok/pkg/apis/internalversion"
 	"sigs.k8s.io/kwok/pkg/log"
 	"sigs.k8s.io/kwok/pkg/utils/expression"
-	"sigs.k8s.io/kwok/pkg/utils/maps"
 )
 
 // NodeController is a fake nodes implementation that can be used to test
@@ -57,13 +57,13 @@ type NodeController struct {
 	manageNodesWithLabelSelector          string
 	nodeSelectorFunc                      func(node *corev1.Node) bool
 	lockPodsOnNodeFunc                    func(ctx context.Context, nodeName string) error
-	nodesSets                             maps.SyncMap[string, *NodeInfo]
+	nodesSets                             *stringSets
 	renderer                              *renderer
 	nodeChan                              chan *corev1.Node
 	parallelTasks                         *parallelTasks
 	lifecycle                             Lifecycle
 	cronjob                               *cron.Cron
-	delayJobsCancels                      maps.SyncMap[string, cron.DoFunc]
+	delayJobsCancels                      sync.Map
 	recorder                              record.EventRecorder
 }
 
@@ -118,6 +118,7 @@ func NewNodeController(conf NodeControllerConfig) (*NodeController, error) {
 		nodeIP:                                conf.NodeIP,
 		nodeName:                              conf.NodeName,
 		nodePort:                              conf.NodePort,
+		nodesSets:                             newStringSets(),
 		cronjob:                               cron.NewCron(),
 		lifecycle:                             lifecycles,
 		parallelTasks:                         newParallelTasks(conf.LockNodeParallelism),
@@ -239,7 +240,7 @@ func (c *NodeController) WatchNodes(ctx context.Context, ch chan<- *corev1.Node,
 					}
 				case watch.Deleted:
 					node := event.Object.(*corev1.Node)
-					if _, has := c.nodesSets.Load(node.Name); has {
+					if c.nodesSets.Has(node.Name) {
 						c.nodesSets.Delete(node.Name)
 					}
 				}
@@ -358,7 +359,7 @@ func (c *NodeController) LockNode(ctx context.Context, node *corev1.Node) error
 		return fmt.Errorf("stage match: %w", err)
 	}
 	if stage == nil {
-		logger.Debug("Skip node",
+		logger.Info("Skip node",
 			"reason", "not match any stages",
 		)
 		return nil
@@ -395,7 +396,7 @@ func (c *NodeController) LockNode(ctx context.Context, node *corev1.Node) error
 				return
 			}
 			if patch == nil {
-				logger.Debug("Skip node",
+				logger.Info("Skip node",
 					"reason", "do not need to modify",
 				)
 			} else {
@@ -412,23 +413,23 @@ func (c *NodeController) LockNode(ctx context.Context, node *corev1.Node) error
 		return nil
 	}
 
-	logger.Debug("Delayed play stage",
+	logger.Info("Delayed play stage",
 		"delay", delay,
 		"stage", stageName,
 	)
 	cancelFunc, ok := c.cronjob.AddWithCancel(cron.Order(time.Now().Add(delay)), func() {
 		c.parallelTasks.Add(func() {
-			cancelOld, ok := c.delayJobsCancels.LoadAndDelete(key)
+			old, ok := c.delayJobsCancels.LoadAndDelete(key)
 			if ok {
-				cancelOld()
+				old.(cron.DoFunc)()
 			}
 			do()
 		})
 	})
 	if ok {
-		cancelOld, ok := c.delayJobsCancels.LoadOrStore(key, cancelFunc)
+		old, ok := c.delayJobsCancels.LoadOrStore(key, cancelFunc)
 		if ok {
-			cancelOld()
+			old.(cron.DoFunc)()
 		}
 	}
 	return nil
@@ -525,16 +526,15 @@ func (c *NodeController) putNodeInfo(node *corev1.Node) {
 			}
 		}
 	}
-	nodeInfo := &NodeInfo{
+	nodeInfo := NodeInfo{
 		HostIPs: nodeIPs,
 	}
-	c.nodesSets.Store(node.Name, nodeInfo)
+	c.nodesSets.Put(node.Name, nodeInfo)
 }
 
 // Has returns true if the node is existed
 func (c *NodeController) Has(nodeName string) bool {
-	_, has := c.nodesSets.Load(nodeName)
-	return has
+	return c.nodesSets.Has(nodeName)
 }
 
 // Size returns the number of nodes
@@ -543,10 +543,10 @@ func (c *NodeController) Size() int {
 }
 
 // Get returns Has bool and corev1.Node if the node is existed
-func (c *NodeController) Get(nodeName string) (*NodeInfo, bool) {
-	nodeInfo, has := c.nodesSets.Load(nodeName)
-	if has {
+func (c *NodeController) Get(nodeName string) (NodeInfo, bool) {
+	value, has := c.nodesSets.Get(nodeName)
+	if nodeInfo, converted := value.(NodeInfo); has && converted {
 		return nodeInfo, has
 	}
-	return nil, has
+	return NodeInfo{}, has
 }
diff --git a/pkg/kwok/controllers/pod_controller.go b/pkg/kwok/controllers/pod_controller.go
index 8ae00320..c24e2de3 100644
--- a/pkg/kwok/controllers/pod_controller.go
+++ b/pkg/kwok/controllers/pod_controller.go
@@ -22,6 +22,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
+	"sync"
 	"text/template"
 	"time"
 
@@ -43,7 +44,6 @@ import (
 	"sigs.k8s.io/kwok/pkg/kwok/cni"
 	"sigs.k8s.io/kwok/pkg/log"
 	"sigs.k8s.io/kwok/pkg/utils/expression"
-	"sigs.k8s.io/kwok/pkg/utils/maps"
 )
 
 var (
@@ -59,14 +59,15 @@ type PodController struct {
 	disregardStatusWithLabelSelector      labels.Selector
 	nodeIP                                string
 	cidrIPNet                             *net.IPNet
-	nodeGetFunc                           func(nodeName string) (*NodeInfo, bool)
+	nodeHasFunc                           func(nodeName string) bool
+	nodeGetFunc                           func(nodeName string) (NodeInfo, bool)
 	ipPool                                *ipPool
 	renderer                              *renderer
 	lockPodChan                           chan *corev1.Pod
 	parallelTasks                         *parallelTasks
 	lifecycle                             Lifecycle
 	cronjob                               *cron.Cron
-	delayJobsCancels                      maps.SyncMap[string, cron.DoFunc]
+	delayJobsCancels                      sync.Map
 	recorder                              record.EventRecorder
 }
 
@@ -78,7 +79,8 @@ type PodControllerConfig struct {
 	DisregardStatusWithLabelSelector      string
 	NodeIP                                string
 	CIDR                                  string
-	NodeGetFunc                           func(nodeName string) (*NodeInfo, bool)
+	NodeHasFunc                           func(nodeName string) bool
+	NodeGetFunc                           func(nodeName string) (NodeInfo, bool)
 	Stages                                []*internalversion.Stage
 	LockPodParallelism                    int
 	FuncMap                               template.FuncMap
@@ -115,6 +117,7 @@ func NewPodController(conf PodControllerConfig) (*PodController, error) {
 		nodeIP:                                conf.NodeIP,
 		cidrIPNet:                             cidrIPNet,
 		ipPool:                                newIPPool(cidrIPNet),
+		nodeHasFunc:                           conf.NodeHasFunc,
 		nodeGetFunc:                           conf.NodeGetFunc,
 		cronjob:                               cron.NewCron(),
 		lifecycle:                             lifecycles,
@@ -242,7 +245,7 @@ func (c *PodController) LockPod(ctx context.Context, pod *corev1.Pod) error {
 		return fmt.Errorf("stage match: %w", err)
 	}
 	if stage == nil {
-		logger.Debug("Skip pod",
+		logger.Info("Skip pod",
 			"reason", "not match any stages",
 		)
 		return nil
@@ -279,7 +282,7 @@ func (c *PodController) LockPod(ctx context.Context, pod *corev1.Pod) error {
 				return
 			}
 			if patch == nil {
-				logger.Debug("Skip pod",
+				logger.Info("Skip pod",
 					"reason", "do not need to modify",
 				)
 			} else {
@@ -296,23 +299,23 @@ func (c *PodController) LockPod(ctx context.Context, pod *corev1.Pod) error {
 		return nil
 	}
 
-	logger.Debug("Delayed play stage",
+	logger.Info("Delayed play stage",
 		"delay", delay,
 		"stage", stageName,
 	)
 	cancelFunc, ok := c.cronjob.AddWithCancel(cron.Order(time.Now().Add(delay)), func() {
 		c.parallelTasks.Add(func() {
-			cancelOld, ok := c.delayJobsCancels.LoadAndDelete(key)
+			old, ok := c.delayJobsCancels.LoadAndDelete(key)
 			if ok {
-				cancelOld()
+				old.(cron.DoFunc)()
 			}
 			do()
 		})
 	})
 	if ok {
-		cancelOld, ok := c.delayJobsCancels.LoadOrStore(key, cancelFunc)
+		old, ok := c.delayJobsCancels.LoadOrStore(key, cancelFunc)
 		if ok {
-			cancelOld()
+			old.(cron.DoFunc)()
 		}
 	}
 
@@ -358,7 +361,7 @@ func (c *PodController) LockPods(ctx context.Context, pods <-chan *corev1.Pod) {
 }
 
 func (c *PodController) needLockPod(pod *corev1.Pod) bool {
-	if _, has := c.nodeGetFunc(pod.Spec.NodeName); !has {
+	if !c.nodeHasFunc(pod.Spec.NodeName) {
 		return false
 	}
 
@@ -413,8 +416,8 @@ func (c *PodController) WatchPods(ctx context.Context, lockChan chan<- *corev1.P
 					if c.needLockPod(pod) {
 						lockChan <- pod.DeepCopy()
 					} else {
-						logger.Debug("Skip pod",
-							"reason", "not managed",
+						logger.Info("Skip pod",
+							"reason", "not manage",
 							"event", event.Type,
 							"pod", log.KObj(pod),
 							"node", pod.Spec.NodeName,
@@ -423,18 +426,16 @@ func (c *PodController) WatchPods(ctx context.Context, lockChan chan<- *corev1.P
 
 				case watch.Deleted:
 					pod := event.Object.(*corev1.Pod)
-					// Recycling PodIP
-					if !pod.Spec.HostNetwork {
-						if _, has := c.nodeGetFunc(pod.Spec.NodeName); has {
-							if !c.enableCNI {
-								if pod.Status.PodIP != "" && c.cidrIPNet.Contains(net.ParseIP(pod.Status.PodIP)) {
-									c.ipPool.Put(pod.Status.PodIP)
-								}
-							} else {
-								err := cni.Remove(context.Background(), string(pod.UID), pod.Name, pod.Namespace)
-								if err != nil {
-									logger.Error("cni remove", err)
-								}
+					if c.nodeHasFunc(pod.Spec.NodeName) && !pod.Spec.HostNetwork {
+						if !c.enableCNI {
+							// Recycling PodIP
+							if pod.Status.PodIP != "" && c.cidrIPNet.Contains(net.ParseIP(pod.Status.PodIP)) {
+								c.ipPool.Put(pod.Status.PodIP)
+							}
+						} else {
+							err := cni.Remove(context.Background(), string(pod.UID), pod.Name, pod.Namespace)
+							if err != nil {
+								logger.Error("cni remove", err)
 							}
 						}
 					}
diff --git a/pkg/kwok/controllers/renderer.go b/pkg/kwok/controllers/renderer.go
index 74086521..8f6faf1a 100644
--- a/pkg/kwok/controllers/renderer.go
+++ b/pkg/kwok/controllers/renderer.go
@@ -25,12 +25,10 @@ import (
 	"text/template"
 
 	"sigs.k8s.io/yaml"
-
-	"sigs.k8s.io/kwok/pkg/utils/maps"
 )
 
 type renderer struct {
-	cache      maps.SyncMap[string, *template.Template]
+	cache      sync.Map
 	bufferPool sync.Pool
 	funcMap    template.FuncMap
 }
@@ -38,6 +36,7 @@ type renderer struct {
 func newRenderer(funcMap template.FuncMap) *renderer {
 	return &renderer{
 		funcMap: funcMap,
+		cache:   sync.Map{},
 		bufferPool: sync.Pool{
 			New: func() interface{} {
 				return bytes.NewBuffer(make([]byte, 4*1024))
@@ -49,15 +48,16 @@ func newRenderer(funcMap template.FuncMap) *renderer {
 // renderToJSON renders the template with the given text and original object.
 func (r *renderer) renderToJSON(text string, original interface{}) ([]byte, error) {
 	text = strings.TrimSpace(text)
-	temp, ok := r.cache.Load(text)
+	v, ok := r.cache.Load(text)
 	if !ok {
-		var err error
-		temp, err = template.New("_").Funcs(r.funcMap).Parse(text)
+		temp, err := template.New("_").Funcs(r.funcMap).Parse(text)
 		if err != nil {
 			return nil, err
 		}
 		r.cache.Store(text, temp)
+		v = temp
 	}
+	temp := v.(*template.Template)
 	buf := r.bufferPool.Get().(*bytes.Buffer)
 	defer r.bufferPool.Put(buf)
 
diff --git a/pkg/kwok/controllers/utils.go b/pkg/kwok/controllers/utils.go
index d696e010..3b6ff82e 100644
--- a/pkg/kwok/controllers/utils.go
+++ b/pkg/kwok/controllers/utils.go
@@ -160,6 +160,57 @@ func (p *parallelTasks) Wait() {
 	p.wg.Wait()
 }
 
+type stringSets struct {
+	mut  sync.RWMutex
+	sets map[string]any
+}
+
+func newStringSets() *stringSets {
+	return &stringSets{
+		sets: make(map[string]any),
+	}
+}
+
+func (s *stringSets) Size() int {
+	s.mut.RLock()
+	defer s.mut.RUnlock()
+	return len(s.sets)
+}
+
+func (s *stringSets) Put(key string, value any) {
+	s.mut.Lock()
+	defer s.mut.Unlock()
+	s.sets[key] = value
+}
+
+func (s *stringSets) Delete(key string) {
+	s.mut.Lock()
+	defer s.mut.Unlock()
+	delete(s.sets, key)
+}
+
+func (s *stringSets) Has(key string) bool {
+	s.mut.RLock()
+	defer s.mut.RUnlock()
+	_, ok := s.sets[key]
+	return ok
+}
+
+func (s *stringSets) Foreach(f func(string)) {
+	s.mut.RLock()
+	defer s.mut.RUnlock()
+	for k := range s.sets {
+		f(k)
+	}
+}
+
+func (s *stringSets) Get(key string) (any, bool) {
+	s.mut.RLock()
+	defer s.mut.RUnlock()
+	value, ok := s.sets[key]
+	return value, ok
+}
+
 func labelsParse(selector string) (labels.Selector, error) {
 	if selector == "" {
 		return nil, nil
diff --git a/pkg/log/logger.go b/pkg/log/logger.go
index bc0651dd..87b2ad7f 100644
--- a/pkg/log/logger.go
+++ b/pkg/log/logger.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"time"
 
 	"golang.org/x/exp/slog"
 	"golang.org/x/term"
@@ -30,12 +29,6 @@ import (
 // IsTerminal returns true if the given file descriptor is a terminal.
 var IsTerminal = term.IsTerminal
 
-// DurationFormat is the format used to print time.Duration in both nanosecond and string.
-type DurationFormat struct {
-	Nanosecond int64  `json:"nanosecond"`
-	Human      string `json:"human"`
-}
-
 // FromContext returns the Logger associated with ctx, or the default logger.
 func FromContext(ctx context.Context) *Logger {
 	return wrapSlog(slog.FromContext(ctx))
@@ -63,14 +56,6 @@ func NewLogger(w io.Writer, level slog.Level) *Logger {
 		AddSource: true,
 		Level:     level,
 		ReplaceAttr: func(a slog.Attr) slog.Attr {
-			if a.Value.Kind() == slog.DurationKind {
-				if t, ok := a.Value.Any().(time.Duration); ok {
-					return slog.Any(a.Key, DurationFormat{
-						Nanosecond: int64(t),
-						Human:      t.String(),
-					})
-				}
-			}
 			if a.Value.Kind() == slog.AnyKind {
 				if t, ok := a.Value.Any().(fmt.Stringer); ok {
 					return slog.Attr{
diff --git a/pkg/utils/maps/doc.go b/pkg/utils/maps/doc.go
deleted file mode 100644
index 9024b43f..00000000
--- a/pkg/utils/maps/doc.go
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
-Copyright 2023 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 maps provides utilities to manipulate maps.
-package maps
diff --git a/pkg/utils/maps/sync.go b/pkg/utils/maps/sync.go
deleted file mode 100644
index 56be819f..00000000
--- a/pkg/utils/maps/sync.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-Copyright 2023 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 maps
-
-import (
-	"sync"
-)
-
-// SyncMap is a wrapper around sync.Map that provides a few additional methods.
-type SyncMap[K comparable, V any] struct {
-	m sync.Map
-}
-
-// Load returns the value stored in the map for a key,
-// or nil if no  value is present.
-func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
-	v, ok := m.m.Load(key)
-	if !ok {
-		return value, false
-	}
-	return v.(V), true
-}
-
-// Store sets the value for a key.
-func (m *SyncMap[K, V]) Store(key K, value V) {
-	m.m.Store(key, value)
-}
-
-// Delete deletes the value for a key.
-func (m *SyncMap[K, V]) Delete(key K) {
-	m.m.Delete(key)
-}
-
-// Range calls f sequentially for each key and value present in the map.
-func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) {
-	m.m.Range(func(key, value interface{}) bool {
-		return f(key.(K), value.(V))
-	})
-}
-
-// LoadAndDelete deletes the value for a key, returning the previous value if any.
-func (m *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
-	v, loaded := m.m.LoadAndDelete(key)
-	if !loaded {
-		return value, loaded
-	}
-	return v.(V), loaded
-}
-
-// LoadOrStore returns the existing value for the key if present.
-func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
-	v, loaded := m.m.LoadOrStore(key, value)
-	if loaded {
-		return v.(V), loaded
-	}
-	return value, loaded
-}
-
-// Size returns the number of items in the map.
-func (m *SyncMap[K, V]) Size() int {
-	size := 0
-	m.m.Range(func(key, value interface{}) bool {
-		size++
-		return true
-	})
-	return size
-}
diff --git a/site/content/en/docs/contributing/getting-started.md b/site/content/en/docs/contributing/getting-started.md
index 03a21dd1..00ad2b70 100644
--- a/site/content/en/docs/contributing/getting-started.md
+++ b/site/content/en/docs/contributing/getting-started.md
@@ -39,7 +39,7 @@ If you do not see anything, please file a [new issue] or [new discussion].
 
 The maintainers of this project are reachable via:
 
-- [Kubernetes Slack] in [#kwok], [#kwok-dev] or [#sig-scheduling] channel
+- [Kubernetes Slack] in the [#sig-scheduling] channel
 - The issue tracker by [filing an issue][new issue]
 - The discussion tracker by [filing a discussion][new discussion]
 - The Kubernetes [SIG-Scheduling] [Mailing List]
@@ -61,8 +61,6 @@ If you're looking to contribute documentation improvements, you'll want to read
 [install docker]: https://docs.docker.com/install/#supported-platforms
 [Kubernetes Slack]: https://slack.k8s.io/
 [#sig-scheduling]: https://kubernetes.slack.com/messages/sig-scheduling/
-[#kwok]: https://kubernetes.slack.com/messages/kwok/
-[#kwok-dev]: https://kubernetes.slack.com/messages/kwok-dev/
 [SIG-Scheduling]: https://github.com/kubernetes/community/blob/master/sig-scheduling/README.md
 [Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-scheduling
 [Kubernetes Contributor Guide]: https://git.k8s.io/community/contributors/guide


Test patch:
diff --git a/pkg/kwok/controllers/pod_controller_test.go b/pkg/kwok/controllers/pod_controller_test.go
index 0a99ff5c..3d14bea0 100644
--- a/pkg/kwok/controllers/pod_controller_test.go
+++ b/pkg/kwok/controllers/pod_controller_test.go
@@ -94,8 +94,8 @@ func TestPodController(t *testing.T) {
 	nodeHasFunc := func(nodeName string) bool {
 		return strings.HasPrefix(nodeName, "node")
 	}
-	nodeGetFunc := func(nodeName string) (*NodeInfo, bool) {
-		node := &NodeInfo{HostIPs: []string{defaultNodeIP}}
+	nodeGetFunc := func(nodeName string) (NodeInfo, bool) {
+		node := NodeInfo{HostIPs: []string{defaultNodeIP}}
 		return node, nodeHasFunc(nodeName)
 	}
 	podStageStatus, _ := NewStagesFromYaml([]byte(stages.DefaultPodStages))
@@ -106,6 +106,7 @@ func TestPodController(t *testing.T) {
 		CIDR:                                  "10.0.0.1/24",
 		DisregardStatusWithAnnotationSelector: annotationSelector.String(),
 		Stages:                                podStageStatus,
+		NodeHasFunc:                           nodeHasFunc,
 		NodeGetFunc:                           nodeGetFunc,
 		FuncMap:                               defaultFuncMap,
 		LockPodParallelism:                    2,