Repository: kubernetes/minikube (go)

Issue:
`minikube addons list` outputs wrong state for default addons
**Reproduce:**
```
$ minikube start --install-addons=false
😄  minikube v1.28.0 on Darwin 13.2 (arm64)
✨  Using the docker driver based on user configuration
📌  Using Docker Desktop driver with root privileges
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
🔥  Creating docker container (CPUs=2, Memory=4000MB) ...
🐳  Preparing Kubernetes v1.25.3 on Docker 20.10.22 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔗  Configuring bridge CNI (Container Networking Interface) ...
🔎  Verifying Kubernetes components...
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

# no storage provisioner
$ kubectl get pods -A
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-565d847f94-hvhfh           1/1     Running   0          5m59s
kube-system   etcd-minikube                      1/1     Running   0          6m12s
kube-system   kube-apiserver-minikube            1/1     Running   0          6m12s
kube-system   kube-controller-manager-minikube   1/1     Running   0          6m12s
kube-system   kube-proxy-tvr7g                   1/1     Running   0          5m59s
kube-system   kube-scheduler-minikube            1/1     Running   0          6m12s

# incorrectly states addons are enabled
$ minikube addons list
|-----------------------------|----------|--------------|--------------------------------|
|         ADDON NAME          | PROFILE  |    STATUS    |           MAINTAINER           |
|-----------------------------|----------|--------------|--------------------------------|
| ambassador                  | minikube | disabled     | 3rd party (Ambassador)         |
| auto-pause                  | minikube | disabled     | Google                         |
| cloud-spanner               | minikube | disabled     | Google                         |
| csi-hostpath-driver         | minikube | disabled     | Kubernetes                     |
| dashboard                   | minikube | disabled     | Kubernetes                     |
| default-storageclass        | minikube | enabled ✅   | Kubernetes                     |
| efk                         | minikube | disabled     | 3rd party (Elastic)            |
| freshpod                    | minikube | disabled     | Google                         |
| gcp-auth                    | minikube | disabled     | Google                         |
| gvisor                      | minikube | disabled     | Google                         |
| headlamp                    | minikube | disabled     | 3rd party (kinvolk.io)         |
| helm-tiller                 | minikube | disabled     | 3rd party (Helm)               |
| inaccel                     | minikube | disabled     | 3rd party (InAccel             |
|                             |          |              | [info@inaccel.com])            |
| ingress                     | minikube | disabled     | Kubernetes                     |
| ingress-dns                 | minikube | disabled     | Google                         |
| istio                       | minikube | disabled     | 3rd party (Istio)              |
| istio-provisioner           | minikube | disabled     | 3rd party (Istio)              |
| kong                        | minikube | disabled     | 3rd party (Kong HQ)            |
| kubevirt                    | minikube | disabled     | 3rd party (KubeVirt)           |
| logviewer                   | minikube | disabled     | 3rd party (unknown)            |
| metallb                     | minikube | disabled     | 3rd party (MetalLB)            |
| metrics-server              | minikube | disabled     | Kubernetes                     |
| nvidia-driver-installer     | minikube | disabled     | Google                         |
| nvidia-gpu-device-plugin    | minikube | disabled     | 3rd party (Nvidia)             |
| olm                         | minikube | disabled     | 3rd party (Operator Framework) |
| pod-security-policy         | minikube | disabled     | 3rd party (unknown)            |
| portainer                   | minikube | disabled     | 3rd party (Portainer.io)       |
| registry                    | minikube | disabled     | Google                         |
| registry-aliases            | minikube | disabled     | 3rd party (unknown)            |
| registry-creds              | minikube | disabled     | 3rd party (UPMC Enterprises)   |
| storage-provisioner         | minikube | enabled ✅   | Google                         |
| storage-provisioner-gluster | minikube | disabled     | 3rd party (Gluster)            |
| volumesnapshots             | minikube | disabled     | Kubernetes                     |
|-----------------------------|----------|--------------|--------------------------------|
```

The addon list should show all addons as disabled

Pull request:
Fix `minikube addons list` output for default addons
Fixes #15710 

The cause of the issue was in [pkg/node/start.go](https://github.com/kubernetes/minikube/blob/master/pkg/minikube/node/start.go#L232) where it only updated the config if there were any existing addons defined which was not the case when `install-addons` was set to false.

It seems to working locally. Adding output here:
```
$ ./minikube-linux-amd64 start --install-addons=false
😄  minikube v1.29.0 on Fedora 36
🆕  Kubernetes 1.26.1 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.26.1
✨  Using the docker driver based on existing profile
❗  docker is currently using the btrfs storage driver, consider switching to overlay2 for better performance
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
🔄  Restarting existing docker container for "minikube" ...
❗  This container is having trouble accessing https://k8s.gcr.io
💡  To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/
🐳  Preparing Kubernetes v1.23.8 on Docker 20.10.20 ...
🔎  Verifying Kubernetes components...
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

# no storage provisioner
$ kubectl get pods -A
NAMESPACE      NAME                                        READY   STATUS             RESTARTS       AGE
kube-system    coredns-64897985d-dvsrj                     1/1     Running            9 (11m ago)    68d
kube-system    etcd-minikube                               1/1     Running            11 (68s ago)   68d
kube-system    kube-apiserver-minikube                     1/1     Running            10 (68s ago)   68d
kube-system    kube-controller-manager-minikube            1/1     Running            9 (68s ago)    68d
kube-system    kube-proxy-mb8r7                            1/1     Running            9 (68s ago)    68d
kube-system    kube-scheduler-minikube                     1/1     Running            10 (68s ago)   68d

# finally correct states for addons
$ ./minikube-linux-amd64 addons list
|-----------------------------|----------|--------------|--------------------------------|
|         ADDON NAME          | PROFILE  |    STATUS    |           MAINTAINER           |
|-----------------------------|----------|--------------|--------------------------------|
| ambassador                  | minikube | disabled     | 3rd party (Ambassador)         |
| auto-pause                  | minikube | disabled     | Google                         |
| cloud-spanner               | minikube | disabled     | Google                         |
| csi-hostpath-driver         | minikube | disabled     | Kubernetes                     |
| dashboard                   | minikube | disabled     | Kubernetes                     |
| default-storageclass        | minikube | disabled     | Kubernetes                     |
| efk                         | minikube | disabled     | 3rd party (Elastic)            |
| freshpod                    | minikube | disabled     | Google                         |
| gcp-auth                    | minikube | disabled     | Google                         |
| gvisor                      | minikube | disabled     | Google                         |
| headlamp                    | minikube | disabled     | 3rd party (kinvolk.io)         |
| helm-tiller                 | minikube | disabled     | 3rd party (Helm)               |
| inaccel                     | minikube | disabled     | 3rd party (InAccel             |
|                             |          |              | [info@inaccel.com])            |
| ingress                     | minikube | disabled     | Kubernetes                     |
| ingress-dns                 | minikube | disabled     | Google                         |
| istio                       | minikube | disabled     | 3rd party (Istio)              |
| istio-provisioner           | minikube | disabled     | 3rd party (Istio)              |
| kong                        | minikube | disabled     | 3rd party (Kong HQ)            |
| kubevirt                    | minikube | disabled     | 3rd party (KubeVirt)           |
| logviewer                   | minikube | disabled     | 3rd party (unknown)            |
| metallb                     | minikube | disabled     | 3rd party (MetalLB)            |
| metrics-server              | minikube | disabled     | Kubernetes                     |
| nvidia-driver-installer     | minikube | disabled     | Google                         |
| nvidia-gpu-device-plugin    | minikube | disabled     | 3rd party (Nvidia)             |
| olm                         | minikube | disabled     | 3rd party (Operator Framework) |
| pod-security-policy         | minikube | disabled     | 3rd party (unknown)            |
| portainer                   | minikube | disabled     | 3rd party (Portainer.io)       |
| registry                    | minikube | disabled     | Google                         |
| registry-aliases            | minikube | disabled     | 3rd party (unknown)            |
| registry-creds              | minikube | disabled     | 3rd party (UPMC Enterprises)   |
| storage-provisioner         | minikube | disabled     | Google                         |
| storage-provisioner-gluster | minikube | disabled     | 3rd party (Gluster)            |
| volumesnapshots             | minikube | disabled     | Kubernetes                     |
|-----------------------------|----------|--------------|--------------------------------|
```


Patch:
diff --git a/pkg/addons/addons.go b/pkg/addons/addons.go
index aacdf43d5..0fd2d0cb1 100644
--- a/pkg/addons/addons.go
+++ b/pkg/addons/addons.go
@@ -538,7 +538,7 @@ func ToEnable(cc *config.ClusterConfig, existing map[string]bool, additional []s
 	// Get the default values of any addons not saved to our config
 	for name, a := range assets.Addons {
 		if _, exists := existing[name]; !exists {
-			enable[name] = a.IsEnabled(cc)
+			enable[name] = a.IsEnabledOrDefault(cc)
 		}
 	}
 
@@ -563,10 +563,18 @@ func ToEnable(cc *config.ClusterConfig, existing map[string]bool, additional []s
 
 // UpdateConfig tries to update config with all enabled addons (not thread-safe).
 // Any error will be logged and it will continue.
-func UpdateConfig(cc *config.ClusterConfig, enabled []string) {
+func UpdateConfigToEnable(cc *config.ClusterConfig, enabled []string) {
 	for _, a := range enabled {
 		if err := Set(cc, a, "true"); err != nil {
 			klog.Errorf("store failed: %v", err)
 		}
 	}
 }
+
+func UpdateConfigToDisable(cc *config.ClusterConfig) {
+	for name := range assets.Addons {
+		if err := Set(cc, name, "false"); err != nil {
+			klog.Errorf("store failed: %v", err)
+		}
+	}
+}
diff --git a/pkg/minikube/assets/addons.go b/pkg/minikube/assets/addons.go
index 8faba03c3..4c7ee492a 100644
--- a/pkg/minikube/assets/addons.go
+++ b/pkg/minikube/assets/addons.go
@@ -80,6 +80,16 @@ func (a *Addon) IsEnabled(cc *config.ClusterConfig) bool {
 		return status
 	}
 
+	return false
+}
+
+// IsEnabledOrDefault checks if an Addon is enabled for the given profile. If not found in profile it returns the default state
+func (a *Addon) IsEnabledOrDefault(cc *config.ClusterConfig) bool {
+	status, ok := cc.Addons[a.Name()]
+	if ok {
+		return status
+	}
+
 	// Return the default unconfigured state of the addon
 	return a.enabled
 }
diff --git a/pkg/minikube/node/start.go b/pkg/minikube/node/start.go
index 2d1810121..573c03983 100644
--- a/pkg/minikube/node/start.go
+++ b/pkg/minikube/node/start.go
@@ -232,8 +232,10 @@ func Start(starter Starter, apiServer bool) (*kubeconfig.Settings, error) {
 	if starter.ExistingAddons != nil {
 		klog.Infof("waiting for cluster config update ...")
 		if ea, ok := <-enabledAddons; ok {
-			addons.UpdateConfig(starter.Cfg, ea)
+			addons.UpdateConfigToEnable(starter.Cfg, ea)
 		}
+	} else {
+		addons.UpdateConfigToDisable(starter.Cfg)
 	}
 
 	// Write enabled addons to the config before completion


Test patch:
diff --git a/pkg/addons/addons_test.go b/pkg/addons/addons_test.go
index bc2814b77..9ea5046e2 100644
--- a/pkg/addons/addons_test.go
+++ b/pkg/addons/addons_test.go
@@ -115,7 +115,7 @@ func TestSetAndSave(t *testing.T) {
 	}
 }
 
-func TestStart(t *testing.T) {
+func TestStartWithAddonsEnabled(t *testing.T) {
 	// this test will write a config.json into MinikubeHome, create a temp dir for it
 	tests.MakeTempDir(t)
 
@@ -133,10 +133,30 @@ func TestStart(t *testing.T) {
 	go Enable(&wg, cc, toEnable, enabled)
 	wg.Wait()
 	if ea, ok := <-enabled; ok {
-		UpdateConfig(cc, ea)
+		UpdateConfigToEnable(cc, ea)
 	}
 
 	if !assets.Addons["dashboard"].IsEnabled(cc) {
 		t.Errorf("expected dashboard to be enabled")
 	}
 }
+
+func TestStartWithAllAddonsDisabled(t *testing.T) {
+	// this test will write a config.json into MinikubeHome, create a temp dir for it
+	tests.MakeTempDir(t)
+
+	cc := &config.ClusterConfig{
+		Name:             "start",
+		CPUs:             2,
+		Memory:           2500,
+		KubernetesConfig: config.KubernetesConfig{},
+	}
+
+	UpdateConfigToDisable(cc)
+
+	for name := range assets.Addons {
+		if assets.Addons[name].IsEnabled(cc) {
+			t.Errorf("expected %s to be disabled", name)
+		}
+	}
+}