sparse-intern-71089
11/03/2022, 2:50 PMbillowy-army-68599
it may contain login secrets, so we shouldn’t save it as a resource optionYou can mark them as a secret
const provider = k8s.Provider("example" {
kubeconfig: pulumi.secret(kubeconfig)
})
If it’s a local path - then it will not be found on pulumi refresh on a different machineyes, it’s not a good idea to make it a local path
millions-train-91139
11/03/2022, 2:55 PMpulumi up
it overrides the local file path on the invoker’s machine
When you do pulumi refresh
it reads the value from the config /home/NotMyUser/.kube/config
and tries to find it on the invokers machine - fails miserably with a very non indicative message:
warning: configured Kubernetes cluster is unreachable: failed to parse kubeconfig data in `kubernetes:config:kubeconfig`- couldn't get version/kind; json parse error: json: cannot unmarshal string into Go value of type struct { APIVersion string "json:\"apiVersion,omitempty\""; Kind string "json:\"kind,omitempty\"" }
error: Preview failed: failed to read resource state due to unreachable cluster. If the cluster has been deleted, you can edit the pulumi state to remove this resource
millions-train-91139
11/03/2022, 2:59 PM// Note: the Python SDK was setting the kubeconfig value to "" by default, so explicitly check for empty string.
if pathOrContents, ok := vars["kubernetes:config:kubeconfig"]; ok && pathOrContents != "" {
var contents string
// Handle the '~' character if it is set in the config string. Normally, this would be expanded by the shell
// into the user's home directory, but we have to do that manually if it is set in a config value.
if pathOrContents == "~" {
// In case of "~", which won't be caught by the "else if"
pathOrContents = homeDir()
} else if strings.HasPrefix(pathOrContents, "~/") {
pathOrContents = filepath.Join(homeDir(), pathOrContents[2:])
}
// If the variable is a valid filepath, load the file and parse the contents as a k8s config.
_, err := os.Stat(pathOrContents)
if err == nil {
b, err := ioutil.ReadFile(pathOrContents)
if err != nil {
unreachableCluster(err)
} else {
contents = string(b)
}
} else { // Assume the contents are a k8s config.
contents = pathOrContents
}
// Load the contents of the k8s config.
apiConfig, err = clientcmd.Load([]byte(contents))
if err != nil {
unreachableCluster(err)
} else {
kubeconfig = clientcmd.NewDefaultClientConfig(*apiConfig, overrides)
configurationNamespace, _, err := kubeconfig.Namespace()
if err == nil {
k.defaultNamespace = configurationNamespace
}
}
}
millions-train-91139
11/03/2022, 3:00 PMkubeconfig
is even necessary if it is always overridden on pulumi up
.
What is the reason we save it as a provider resource optionbillowy-army-68599
billowy-army-68599
millions-train-91139
11/03/2022, 3:03 PMmillions-train-91139
11/03/2022, 3:04 PMpulumi up
that’s why pulumi up
always works, it uses the the local kubeconfig and overrides the path of the resource tooquaint-eye-38036
11/03/2022, 3:27 PMconst provider = k8s.Provider("example" {
kubeconfig: pulumi.secret(kubeconfig)
})
billowy-army-68599
import pulumi
import pulumi_kubernetes as k8s
kubeconfig = pulumi.Output.secret(f = open('kubeconfig.json'))
provider = k8s.provider.Provider(
kubeconfig=kubeconfig
)
(untested)quaint-eye-38036
11/03/2022, 3:36 PMmillions-train-91139
11/04/2022, 12:43 PMpulumi-kubernetes
but I wanted to make sure this is indeed a bug and not something I don’t understand.
Kubeconfig
gets populated by default with a path that breaks pulumi refresh
on different machines.
In addition every pulumi up
ignores Kubeconfig
and overwrites it.
So for my perspective - Kubeconfig
should be deleted from the provider, as it makes no sense, but there is so much code around that in pulumi-kubernetes
that makes me think that I might be missing something.
Either no one uses pulumi-kubernetes
Either no one uses pulumi refresh
with pulumi-kubernetes
Either everyone that runs pulumi-kubernetes
and uses pulumi refresh
does it from the same machine.
Just wanted so ask around the community before I open a PRbillowy-army-68599
Kubeconfig gets populated by default with a path that breaks pulumi refresh on different machines.This is expected behaviour the path to a kubeconfig gets stored in your state.
Either no one uses pulumi-kubernetesobviously not true 🙂
Either no one uses pulumi refresh with pulumi-kubernetesAlso not true 🙂
Either everyone that runs pulumi-kubernetes and uses pulumi refresh does it from the same machine.Not likely The actual reason is more like
Everyone who uses pulumi-kubernetes doesn’t hardcode paths in the kubeconfig. They either use the ambient provider (ie, using theenvironment variable) or they pass the kubeconfig from a created clusterKUBECONFIG
millions-train-91139
11/04/2022, 4:31 PMKUBECONFIG
environment variable, and then the path gets resolved into the provider state.
A refresh on a different machine uses that provider’s state, but will not find the kubeconfig file - which, by the way, is not guaranteed to have the same information.
So saving a path (default) in Kubeconfig doesn’t make much sense.
Saving the contents of the local Kubeconfig file in that state makes more sense, since some of the cluster information is saved there, but then on the other hand, part of the Kubeconfig has secret auth tokens (with expiry) that probably shouldn’t be saved by Pulumi.
---
So let’s say we want to fix pulumi refresh
failing for us.
We have two options:
One - without code changes:
1. Use same path on all machines that will store the configuration for our cluster - a bit non standard
2. Use unexpanded kubeconfig=“~/.kube/config” - slightly better option that 1 since it will default to different home dir across systems
3. Put the contents of the Kubeconfig in the configuration - I don’t think it will work nicely after the credentials expire.
So option number 2 makes most sense, but we need to make sure across all developer/CI machines that we use ~/.kube/config as the path
Two - with code changes:
1. Read local kubeconfig on pulumi refresh
- not sure how to prioritize between local one to saved one.
Regardless - we need to fix:
1. Non indicative error message that happens when saved kubeconfig path is not found locally / or access to it is denied
2. Fix the “if file was not found, treat file path as Kubeconfig contents and fail miserably.
I would first try to understand if its contents by checking if it starts with “apiVersion:” or something (maybe if it contains \n).
Then if its a single line file path - then treat it as a file and if its not found - write that it’s not found.
Need to be careful around printing secrets though.millions-train-91139
11/04/2022, 4:38 PMKUBECONFIG=~/.kube/config
This bug might never arise for refresh.
But if people:
KUBECONFIG=/home/sam/.kube/config
Then pulumi refresh
will not work on other machines (without the user sam and access to sam’s folder)billowy-army-68599
billowy-army-68599
millions-train-91139
11/04/2022, 4:49 PMKUBECONFIG=/path pulumi up
Will go through each SDK differently
But they all do the same:
if isZero(args.Kubeconfig) {
args.Kubeconfig = pulumi.StringPtr(getEnvOrDefault("", nil, "KUBECONFIG").(string))
}
millions-train-91139
11/04/2022, 4:53 PMKUBECONFIG=~/.kube/config pulumi up
Will be saved as Kubeconfig: ~/.kube/config
and then pulumi refresh
will work on other machines that use home dir
KUBECONFIG=/home/sam/.kube/config pulumi up
Which might be expanded in the shell by mistake
Will be saved as Kubeconfig: /home/sam/.kube/config
and then pulumi refresh
won’t work on other machines that do not belong to sam
KUBECONFIG= pulumi up
Will be saved as Kubeconfig: ""
and looks like pulumi refresh
should work.
Since
if pathOrContents, ok := vars["kubernetes:config:kubeconfig"]; ok && pathOrContents != "" {
is false.
but I think it didn’t work on pulumi up
since kubeconfig was not foundmillions-train-91139
11/04/2022, 4:57 PMpulumi-kubernetes
users have:
KUBECONFIG=~/.kube/config
configured.
And we stepped on the:
KUBECONFIG=/home/sam/.kube/config
landmine
In any case the error message needs to be fixed from:
warning: configured Kubernetes cluster is unreachable: failed to parse kubeconfig data in `kubernetes:config:kubeconfig`- couldn't get version/kind; json parse error: json: cannot unmarshal string into Go value of type struct { APIVersion string "json:\"apiVersion,omitempty\""; Kind string "json:\"kind,omitempty\"" }
error: Preview failed: failed to read resource state due to unreachable cluster. If the cluster has been deleted, you can edit the pulumi state to remove this resource