Code change:
//   3.  CurrentDirectoryPath //   4.  HomeDirectoryPath // Empty filenames are ignored.  Files with non-deserializable content produced errors. <del> // The first file to set a particular value or map key wins and the value or map key is never changed. // This means that the first file to set CurrentContext will have its context preserved.  It also means // that if two files specify a "red-user", only values from the first file's red-user are used.  Even </del> <ins> // The first file to set a particular map key wins and map key's value is never changed. // BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed. // This results in some odd looking logic to merge in one direction, merge in the other, and then merge the two. // It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even </ins> // non-conflicting entries from the second file's "red-user" are discarded. // Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder // and only absolute file paths are returned. func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { <del> config := clientcmdapi.NewConfig() mergeConfigWithFile(config, rules.CommandLinePath) resolveLocalPaths(rules.CommandLinePath, config) </del> <ins> kubeConfigFiles := []string{rules.CommandLinePath, rules.EnvVarPath, rules.CurrentDirectoryPath, rules.HomeDirectoryPath} </ins> <del> mergeConfigWithFile(config, rules.EnvVarPath) resolveLocalPaths(rules.EnvVarPath, config) </del> <ins> // first merge all of our maps mapConfig := clientcmdapi.NewConfig() for _, file := range kubeConfigFiles { mergeConfigWithFile(mapConfig, file) resolveLocalPaths(file, mapConfig) } </ins> <del> mergeConfigWithFile(config, rules.CurrentDirectoryPath) resolveLocalPaths(rules.CurrentDirectoryPath, config) </del> <ins> // merge all of the struct values in the reverse order so that priority is given correctly nonMapConfig := clientcmdapi.NewConfig() for i := len(kubeConfigFiles) - 1; i >= 0; i-- { file := kubeConfigFiles[i] mergeConfigWithFile(nonMapConfig, file) resolveLocalPaths(file, nonMapConfig) } </ins> <del> mergeConfigWithFile(config, rules.HomeDirectoryPath) resolveLocalPaths(rules.HomeDirectoryPath, config) </del> <ins> // since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and // get the values we expect. config := clientcmdapi.NewConfig() mergo.Merge(config, mapConfig) mergo.Merge(config, nonMapConfig) </ins> return config, nil }

Related discussion:
text: I've been pulling my hair out trying get kubectl talk to my local cluster. It seems that kubectl merges the local file with values pulled down from the cloud. I've done the following: The I just set is silently overridden. Questions: is this documented anywhere? should kubectl merge in my GCE cluster when I set my cloud-provider to something else? should kubectl override my previously set current-context?
workaround is to use flag to override the config file value.
but not a good UX long term
Someone else hit this as well: . Better fix is to have the local-up script set a context, which I will try to add today. As for the overwriting behavior, it's documented in , but based on what you describe, the intended load order is not working; local .kubeconfig should overwrite global. cc
That sounds infuriating. I'll take a look this morning.