Repository: knative/pkg (go)

Issue:
kvstore panics on empty map
<!--
Pro-tip: You can leave this block commented, and it still works!

Select the appropriate areas for your issue:

/area API

Classify what kind of issue this is:

/kind bug

-->

## Expected Behavior
Setting a key on an empty kvstore, i.e. ConfigMap with no data field, should not panic.

## Actual Behavior

```bash
panic: assignment to entry in nil map [recovered]
	panic: assignment to entry in nil map
```

## Steps to Reproduce the Problem

1. Create empty ConfigMap (name/namespace only)
2. Use kvstore
3. Set key -> panic

## Additional Info


Pull request:
Fix nil pointer panic in kvstore
Signed-off-by: Michael Gasch <mgasch@vmware.com>

<!-- Thanks for sending a pull request! -->

# Changes

<!-- 
Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! 

- :bug: Fix bug
-->

- Fix bug in kvstore causing nil pointer panic on an empty map (map without data)

<!--
In addition, categorize the changes you're making using the "/kind" Prow command, example:

/kind <kind>

Supported kinds are: api-change, bug, cleanup, deprecation, removal, documentation, enhancement, performance

-->
/kind  bug

<!--
*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 #2001 

<!-- Please include the 'why' behind your changes if no issue exists -->

**Release Note**

<!--
:page_facing_up: If this change has user-visible impact, write a release note in the block
below. Include the string "action required" if additional action is required of
users switching to the new release, for example in case of a breaking change.

Write as if you are speaking to users, not other Knative contributors. If this
change has no user-visible impact, no release-note is needed.
-->
```release-note

```

**Docs**

<!--
:book: If this change has user-visible impact, link to an issue or PR in
https://github.com/knative/docs.

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:
- [knative/docs]: <issue or pr link>
- [Feature Track]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```


Patch:
diff --git a/kvstore/kvstore_cm.go b/kvstore/kvstore_cm.go
index d3e383893..de6c4eab3 100644
--- a/kvstore/kvstore_cm.go
+++ b/kvstore/kvstore_cm.go
@@ -103,6 +103,9 @@ func (cs *configMapKVStore) Set(ctx context.Context, key string, value interface
 	if err != nil {
 		return fmt.Errorf("failed to Marshal: %w", err)
 	}
+	if cs.data == nil {
+		cs.data = map[string]string{}
+	}
 	cs.data[key] = string(bytes)
 	return nil
 }


Test patch:
diff --git a/kvstore/kvstore_cm_test.go b/kvstore/kvstore_cm_test.go
index abcb5cae5..39c6b946a 100644
--- a/kvstore/kvstore_cm_test.go
+++ b/kvstore/kvstore_cm_test.go
@@ -131,6 +131,43 @@ func TestLoadSaveUpdate(t *testing.T) {
 	if err != nil {
 		t.Error("failed to return string:", err)
 	}
+	if ret != "otherbar" {
+		t.Errorf("got back unexpected value, wanted %q got %q", "bar", ret)
+	}
+}
+
+func TestLoadSaveUpdateEmptyMap(t *testing.T) {
+	// empty map, i.e. no data field
+	cm := corev1.ConfigMap{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "ConfigMap",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+	}
+
+	tc := newTestClient([]runtime.Object{&cm}...)
+	cs := NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset)
+	if err := cs.Init(context.Background()); err != nil {
+		t.Error("Failed to Init ConfigStore:", err)
+	}
+
+	if err := cs.Set(context.Background(), "jimmy", "otherbar"); err != nil {
+		t.Error("Failed to set key:", err)
+	}
+
+	if err := cs.Save(context.Background()); err != nil {
+		t.Error("Failed to save configmap:", err)
+	}
+
+	if tc.updated == nil {
+		t.Errorf("ConfigMap Not updated")
+	}
+	var ret string
+	err := cs.Get(context.Background(), "jimmy", &ret)
 	if err != nil {
 		t.Error("failed to return string:", err)
 	}
@@ -165,9 +202,6 @@ func TestLoadSaveUpdateComplex(t *testing.T) {
 	if err != nil {
 		t.Error("failed to return string:", err)
 	}
-	if err != nil {
-		t.Error("failed to return string:", err)
-	}
 	if !reflect.DeepEqual(ret, ts2) {
 		t.Errorf("got back unexpected value, wanted %+v got %+v", ts2, ret)
 	}