Skip to content

Commit 6ba3f08

Browse files
dnmsupinodn
authored andcommitted
drivenets: create empty ConfigMap when config mount is defaulted
cdnosDefaults populates pb.Config.ConfigPath ("/config_load") and pb.Config.ConfigFile ("default") on every node, but CreateConfig short-circuits and creates no ConfigMap when no startup data (Config_File / Config_Data) is supplied. The cdnos-controller still mounts the controller-expected "<node>-config" ConfigMap into the pod whenever ConfigPath + ConfigFile are populated on the CR. With the ConfigMap missing, every pod sits forever in PodInitializing with: MountVolume.SetUp failed for volume "config" : configmap "<node>-config" not found This shows up immediately on any topology whose nodes don't ship a startup configuration - e.g. lightweight scale tests using an alpine image where data:/file: is omitted from the proto. Fix: when ConfigPath + ConfigFile are populated but no data was provided, create an empty ConfigMap (single empty key) instead of returning nil. Users who explicitly want to opt out of the config mount can still clear ConfigPath/ConfigFile in the proto, and the existing "no mount" path is preserved. Adds unit tests covering both branches (defaulted-but-empty and fully-cleared).
1 parent ab66ab3 commit 6ba3f08

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

topo/node/drivenets/drivenets.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,16 @@ func (n *Node) CreateConfig(ctx context.Context) (*corev1.Volume, error) {
641641
data = v.Data
642642
}
643643
if data == nil {
644-
return nil, nil
644+
// No startup config was supplied. If the controller-managed Pod
645+
// will nevertheless mount a config ConfigMap (because ConfigPath
646+
// + ConfigFile are populated - typically by cdnosDefaults), we
647+
// must still create an empty ConfigMap so the mount succeeds.
648+
// Otherwise the Pod sits forever in PodInitializing with
649+
// `configmap "<name>-config" not found`.
650+
if pb.Config.GetConfigPath() == "" || pb.Config.GetConfigFile() == "" {
651+
return nil, nil
652+
}
653+
data = []byte{}
645654
}
646655
cm := &corev1.ConfigMap{
647656
ObjectMeta: metav1.ObjectMeta{

topo/node/drivenets/drivenets_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package drivenets
22

33
import (
4+
"context"
45
"testing"
56

67
"github.com/openconfig/gnmi/errdiff"
78
tpb "github.com/openconfig/kne/proto/topo"
89
"github.com/openconfig/kne/topo/node"
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/client-go/kubernetes/fake"
913
)
1014

1115
func TestNew(t *testing.T) {
@@ -108,6 +112,80 @@ func TestCdnosDefaults(t *testing.T) {
108112
}
109113
}
110114

115+
// TestCreateConfig_NoDataButPathDefaulted verifies that an empty ConfigMap
116+
// is still created when no startup data is supplied but cdnosDefaults has
117+
// populated ConfigPath/ConfigFile. Without this, the controller-created Pod
118+
// would block forever in PodInitializing on a missing ConfigMap mount.
119+
func TestCreateConfig_NoDataButPathDefaulted(t *testing.T) {
120+
pb := cdnosDefaults(&tpb.Node{Name: "n1"})
121+
if pb.Config.GetConfigPath() == "" || pb.Config.GetConfigFile() == "" {
122+
t.Fatalf("preconditions: cdnosDefaults should populate ConfigPath and ConfigFile")
123+
}
124+
if pb.Config.GetConfigData() != nil {
125+
t.Fatalf("preconditions: cdnosDefaults should not set startup config data")
126+
}
127+
128+
kc := fake.NewSimpleClientset()
129+
n := &Node{Impl: &node.Impl{
130+
Proto: pb,
131+
KubeClient: kc,
132+
Namespace: "ns1",
133+
}}
134+
135+
vol, err := n.CreateConfig(context.Background())
136+
if err != nil {
137+
t.Fatalf("CreateConfig returned unexpected error: %v", err)
138+
}
139+
if vol == nil {
140+
t.Fatalf("CreateConfig returned nil volume; controller mount would fail")
141+
}
142+
cm, err := kc.CoreV1().ConfigMaps("ns1").Get(context.Background(), "n1-config", metav1.GetOptions{})
143+
if err != nil {
144+
t.Fatalf("expected ConfigMap n1-config to exist: %v", err)
145+
}
146+
if _, ok := cm.Data[pb.Config.GetConfigFile()]; !ok {
147+
t.Errorf("ConfigMap missing key %q; have keys: %v", pb.Config.GetConfigFile(), keys(cm.Data))
148+
}
149+
}
150+
151+
// TestCreateConfig_NoDataAndNoPath verifies the explicit "no config mount"
152+
// path: when neither ConfigPath nor ConfigFile is set, no ConfigMap is
153+
// created.
154+
func TestCreateConfig_NoDataAndNoPath(t *testing.T) {
155+
pb := &tpb.Node{
156+
Name: "n2",
157+
Config: &tpb.Config{}, // empty: no Path, no File, no Data
158+
}
159+
kc := fake.NewSimpleClientset()
160+
n := &Node{Impl: &node.Impl{
161+
Proto: pb,
162+
KubeClient: kc,
163+
Namespace: "ns1",
164+
}}
165+
vol, err := n.CreateConfig(context.Background())
166+
if err != nil {
167+
t.Fatalf("CreateConfig returned unexpected error: %v", err)
168+
}
169+
if vol != nil {
170+
t.Errorf("expected nil volume when no config is requested, got %+v", vol)
171+
}
172+
cms, _ := kc.CoreV1().ConfigMaps("ns1").List(context.Background(), metav1.ListOptions{})
173+
if len(cms.Items) != 0 {
174+
t.Errorf("expected zero ConfigMaps, found %d", len(cms.Items))
175+
}
176+
}
177+
178+
func keys(m map[string]string) []string {
179+
out := make([]string, 0, len(m))
180+
for k := range m {
181+
out = append(out, k)
182+
}
183+
return out
184+
}
185+
186+
// dummy var keeps corev1 imported even if the rest of the file doesn't use it.
187+
var _ = corev1.ConfigMap{}
188+
111189
func TestDefaultNodeConstraints(t *testing.T) {
112190
n := &Node{}
113191
constraints := n.DefaultNodeConstraints()

0 commit comments

Comments
 (0)