-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
app: home: Add delete button for clusters #2567
base: main
Are you sure you want to change the base?
Changes from all commits
4c5b017
8666a77
a6f0e8e
c9d803a
4ec545d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -234,6 +234,18 @@ func serveWithNoCacheHeader(fs http.Handler) http.HandlerFunc { | |
} | ||
} | ||
|
||
// defaultKubeConfigFile returns the default path to the kubeconfig file. | ||
func defaultKubeConfigFile() (string, error) { | ||
homeDir, err := os.UserHomeDir() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get user home directory: %v", err) | ||
} | ||
|
||
kubeConfigFile := filepath.Join(homeDir, ".kube", "config") | ||
|
||
return kubeConfigFile, nil | ||
} | ||
|
||
// defaultKubeConfigPersistenceDir returns the default directory to store kubeconfig | ||
// files of clusters that are loaded in Headlamp. | ||
func defaultKubeConfigPersistenceDir() (string, error) { | ||
|
@@ -1365,6 +1377,126 @@ func (c *HeadlampConfig) addContextsToStore(contexts []kubeconfig.Context, setup | |
return setupErrors | ||
} | ||
|
||
// collectMultiConfigPaths looks at the default dynamic directory | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This commit should be merged with the previous one AFAIU. |
||
// (e.g. ~/.config/Headlamp/kubeconfigs) and returns any files found there. | ||
// This is called from the 'else' block in deleteCluster(). | ||
func (c *HeadlampConfig) collectMultiConfigPaths() ([]string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be moved to |
||
dynamicDir, err := defaultKubeConfigPersistenceDir() | ||
if err != nil { | ||
return nil, fmt.Errorf("getting default kubeconfig persistence dir: %w", err) | ||
} | ||
|
||
entries, err := os.ReadDir(dynamicDir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, the function reads all files from the directory without checking their extensions or format. Can you please add validation for known kubeconfig extensions? |
||
if err != nil { | ||
return nil, fmt.Errorf("reading dynamic kubeconfig directory: %w", err) | ||
} | ||
|
||
var configPaths []string //nolint:prealloc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove this linter and allocate it earlier |
||
|
||
for _, entry := range entries { | ||
// Optionally skip directories or non-kubeconfig files, if needed. | ||
if entry.IsDir() { | ||
continue | ||
} | ||
|
||
filePath := filepath.Join(dynamicDir, entry.Name()) | ||
|
||
configPaths = append(configPaths, filePath) | ||
} | ||
|
||
return configPaths, nil | ||
} | ||
|
||
func removeContextFromDefaultKubeConfig( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please move it to |
||
w http.ResponseWriter, | ||
contextName string, | ||
configPaths ...string, | ||
) error { | ||
// If no specific paths passed, fallback to the default. | ||
if len(configPaths) == 0 { | ||
discoveredPath, err := defaultKubeConfigPersistenceFile() | ||
if err != nil { | ||
logger.Log( | ||
logger.LevelError, | ||
map[string]string{"cluster": contextName}, | ||
err, | ||
"getting default kubeconfig persistence file", | ||
) | ||
http.Error(w, "getting default kubeconfig persistence file", http.StatusInternalServerError) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please do not |
||
|
||
return err | ||
} | ||
|
||
configPaths = []string{discoveredPath} | ||
} | ||
|
||
// Hand off to a small helper function that handles multi-file iteration. | ||
return removeContextFromConfigs(w, contextName, configPaths) | ||
} | ||
|
||
// removeContextFromConfigs does the real iteration over the configPaths. | ||
func removeContextFromConfigs(w http.ResponseWriter, contextName string, configPaths []string) error { | ||
var removed bool | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please check whether contextName is empty or if configPath exists if contextName == "" {
return fmt.Errorf("context name cannot be empty")
}
if len(configPaths) == 0 {
return fmt.Errorf("no config paths provided")
} |
||
for _, filePath := range configPaths { | ||
logger.Log( | ||
logger.LevelInfo, | ||
map[string]string{ | ||
"cluster": contextName, | ||
"kubeConfigPersistenceFile": filePath, | ||
}, | ||
nil, | ||
"Trying to remove context from kubeconfig", | ||
) | ||
|
||
err := kubeconfig.RemoveContextFromFile(contextName, filePath) | ||
if err == nil { | ||
removed = true | ||
|
||
logger.Log(logger.LevelInfo, | ||
map[string]string{"cluster": contextName, "file": filePath}, | ||
nil, "Removed context from kubeconfig", | ||
) | ||
|
||
break | ||
} | ||
|
||
if strings.Contains(err.Error(), "context not found") { | ||
logger.Log(logger.LevelInfo, | ||
map[string]string{"cluster": contextName, "file": filePath}, | ||
nil, "Context not in this file; checking next.", | ||
) | ||
|
||
continue | ||
} | ||
|
||
logger.Log(logger.LevelError, | ||
map[string]string{"cluster": contextName}, | ||
err, "removing cluster from kubeconfig", | ||
) | ||
|
||
http.Error(w, "removing cluster from kubeconfig", http.StatusInternalServerError) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again please remove this |
||
|
||
return err | ||
} | ||
|
||
if !removed { | ||
e := fmt.Errorf("context %q not found in any provided kubeconfig file(s)", contextName) | ||
|
||
logger.Log( | ||
logger.LevelError, | ||
map[string]string{"cluster": contextName}, | ||
e, | ||
"context not found in any file", | ||
) | ||
http.Error(w, e.Error(), http.StatusBadRequest) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
|
||
return e | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// deleteCluster deletes the cluster from the store and updates the kubeconfig file. | ||
func (c *HeadlampConfig) deleteCluster(w http.ResponseWriter, r *http.Request) { | ||
if err := checkHeadlampBackendToken(w, r); err != nil { | ||
|
@@ -1384,28 +1516,42 @@ func (c *HeadlampConfig) deleteCluster(w http.ResponseWriter, r *http.Request) { | |
return | ||
} | ||
|
||
kubeConfigPersistenceFile, err := defaultKubeConfigPersistenceFile() | ||
if err != nil { | ||
logger.Log(logger.LevelError, map[string]string{"cluster": name}, | ||
err, "getting default kubeconfig persistence file") | ||
http.Error(w, "getting default kubeconfig persistence file", http.StatusInternalServerError) | ||
removeKubeConfig := r.URL.Query().Get("removeKubeConfig") == "true" | ||
|
||
return | ||
if removeKubeConfig { | ||
kubeConfigFile, err := defaultKubeConfigFile() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think clusters can be loaded from multiple kubeconfig files, yet we only have a flag telling us whether the clusters from kubeconfig or dynamic. This is a limitation in our backend code (not your PR), but one that we have to handle before we consider this PR IMO. |
||
if err != nil { | ||
logger.Log(logger.LevelError, map[string]string{"cluster": name}, | ||
err, "failed to get default kubeconfig file path") | ||
http.Error(w, "failed to get default kubeconfig file path", http.StatusInternalServerError) | ||
|
||
return | ||
} | ||
|
||
err = kubeconfig.RemoveContextFromFile(name, kubeConfigFile) | ||
if err != nil { | ||
logger.Log(logger.LevelError, map[string]string{"cluster": name}, | ||
err, "removing context from default kubeconfig file") | ||
http.Error(w, "removing context from default kubeconfig file", http.StatusInternalServerError) | ||
|
||
return | ||
} | ||
} | ||
|
||
logger.Log(logger.LevelInfo, map[string]string{ | ||
"cluster": name, | ||
"kubeConfigPersistenceFile": kubeConfigPersistenceFile, | ||
}, | ||
nil, "Removing cluster from kubeconfig") | ||
if !removeKubeConfig { | ||
configPathsList, pathErr := c.collectMultiConfigPaths() | ||
if pathErr != nil { | ||
logger.Log(logger.LevelError, map[string]string{"cluster": name}, | ||
pathErr, "collecting multi config paths") | ||
http.Error(w, "collecting multi config paths", http.StatusInternalServerError) | ||
|
||
err = kubeconfig.RemoveContextFromFile(name, kubeConfigPersistenceFile) | ||
if err != nil { | ||
logger.Log(logger.LevelError, map[string]string{"cluster": name}, | ||
err, "removing cluster from kubeconfig") | ||
http.Error(w, "removing cluster from kubeconfig", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
return | ||
if err := removeContextFromDefaultKubeConfig(w, name, configPathsList...); err != nil { | ||
// removeContextFromDefaultKubeConfig writes any needed http.Error if it fails | ||
return | ||
} | ||
} | ||
|
||
logger.Log(logger.LevelInfo, map[string]string{"cluster": name, "proxy": name}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -665,6 +665,34 @@ func TestRenameCluster(t *testing.T) { | |
} | ||
} | ||
|
||
func TestRemoveContextFromDefaultKubeConfig(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function should also be moved. Thanks |
||
// 1) Create a temp directory for our test kubeconfig | ||
tmpDir := t.TempDir() | ||
mockConfigFile := filepath.Join(tmpDir, "config") | ||
|
||
// 2) Copy "kubeconfig_remove" (which includes 'kubedelta') into that file | ||
testDataPath := filepath.Join("headlamp_testdata", "kubeconfig_remove") | ||
testData, err := os.ReadFile(testDataPath) | ||
require.NoError(t, err, "failed to read test data for 'kubeconfig_remove'") | ||
|
||
err = os.WriteFile(mockConfigFile, testData, 0o600) | ||
require.NoError(t, err, "failed to write test kubeconfig") | ||
|
||
// 3) We need a fake http.ResponseWriter | ||
w := httptest.NewRecorder() | ||
|
||
// 4) Call removeContextFromDefaultKubeConfig with our mock path as the third param | ||
err = removeContextFromDefaultKubeConfig(w, "kubedelta", mockConfigFile) | ||
require.NoError(t, err, "removeContextFromDefaultKubeConfig should succeed") | ||
|
||
// 5) Verify 'kubedelta' is removed from the file | ||
updatedData, err := os.ReadFile(mockConfigFile) | ||
require.NoError(t, err, "failed to read updated kubeconfig") | ||
|
||
require.NotContains(t, string(updatedData), "kubedelta", | ||
"Expected 'kubedelta' context to be removed from kubeconfig") | ||
} | ||
|
||
func TestFileExists(t *testing.T) { | ||
// Test for existing file | ||
assert.True(t, fileExists("./headlamp_testdata/kubeconfig"), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
apiVersion: v1 | ||
kind: Config | ||
current-context: minikubetest | ||
preferences: {} | ||
clusters: | ||
- cluster: | ||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EZ3lOakV4TURRMU0xb1hEVE15TURneU16RXhNRFExTTFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTnk3Ci9kREMxV0w3TXNSWGV2Z2tUQXkzcFZHMVVLa1VQeXd4cS9ETHBPdmRzQmloQjZoVmN1bWNZUTkzYUxLbERzSXMKR0Q0QUJkUFM4cEFPMzhMb3RBWWVDeDIwcDFPem9LYVMvVkp6ZlJKQWVUSStCY3dzRjh2U1VXYU0reWZ4STBPUgpnalE0OVR0eUppYURyS2tzbnd4R3Y0K0U3aWFhZUVPMG55U01EcnpON1RvYkVyb1pObHRzNkdMN2tpTDB0TG5ZCkorNnNtSHlhSGh6WThaR0JZMFdWUXpzNENFMnJ0Q1k5eTV4N2F3bUlDUWE2anBXVFVQazNqa0RMcU93bEQyRmMKcHNkeXI4a1Z3UUhTUUVnRkg2Yzgwdnp3Ny9RSUVDdGRYNlZRRnE1bzYzOWlvc3hQcXVKV3ZtMGVjdkx5dC81cApxNXZpNzMxWThEb0VDMjFtS2NzQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZJNFFkU2FSRFVodi8wWjk0ZzV5RmlVdWlMZHBNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQ3pWWUpBUzQ1UFBOSFVSaDJKWQpKWDFycmFMdGNTbzVuNG1DVy9oeE5YdHpCMlIzWkhPU0hnNmF2R3JNeFY4ZlpCdmtBdEJFaUYzM2JvRThzZzVhCjhhWHRFTjR5bzlZQ2FZc2ZXK2tNNlZDRUdtVWd5bm13aXltYTBzSW5USlZ1R3ZVbDVucVhjUHJJdW9OTVVrTUwKdCsrckxCb0NwY2xrN09VSTA0dXZvanpxc2hsQ0JiMURSOXRwT0s0Kys0UGdPait6OXZ0N3g0dzhMYlhvQmtvegozOEJyVEoyQ3NqbU0xS2ZqZXlpNWdHVmFjeE9YSXRjbXprNzRpQzZ0SjdqVm1MVmNacEc5ZElvcFk5WTBaTkQ0CmQzZjlmOGdCWkJzaXA0a3gxMmFxMlJ5dzFYNGVOaFY2dW5OaCtHVHNhNlFDSlJ0Zk9FK1Q4Njd2ZHlPbjZMb2wKYWQ4PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please change it simple base64 encoded value. Using actual certificate data in test files, even if they are expired or invalid, is not a best practice from a security perspective. I think we should update them in other places as well like https://github.com/headlamp-k8s/headlamp/blob/main/backend/pkg/kubeconfig/test_data/kubeconfig_partialcontextvalid#L31. |
||
extensions: | ||
- extension: | ||
last-update: Mon, 26 Dec 2022 20:33:03 IST | ||
provider: minikubetest.sigs.k8s.io | ||
version: v1.28.0 | ||
name: cluster_info | ||
server: https://127.0.0.1:60279 | ||
name: minikubetest | ||
contexts: | ||
- context: | ||
cluster: minikubetest | ||
namespace: default | ||
user: minikubetest | ||
extensions: | ||
- extension: | ||
last-update: Mon, 26 Dec 2022 20:33:03 IST | ||
provider: minikubetest.sigs.k8s.io | ||
version: v1.28.0 | ||
name: context_info | ||
- extension: | ||
creationTimestamp: null | ||
customName: minikubetestworks | ||
name: headlamp_info | ||
name: minikubetest | ||
- context: | ||
cluster: minikubetest | ||
namespace: default | ||
user: kubedelta | ||
name: kubedelta | ||
users: | ||
- name: minikubetest | ||
user: | ||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJekNDQWd1Z0F3SUJBZ0lJZlJpZk1qZWl1eFV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBNE1qWXhNVEEwTlROYUZ3MHlOREF4TURJeE1ESXhNelZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzRLUmwrS0lsN0NJYVgKbzIwYjdBOVEvaURDbUN6dWFXMSs3WEJyelhiZHNmNkNaRzhVMWZYWTdVWXl3bXVhYkZldUFrUHRBT1hyWVg0YQpMTGZtWTRvdkZYc1RQWmtPUktJeWRFUmNnLy9hOStPd3d2c1ZCUUp4NFplbUtrN1NzaFYxcjl3WGVqVnJIUkFOCm5xQ3JIQVhFNHA5bmFKZHNkTXIyQWdDa0VIK01tTFNqTExNL1lWcnExdmJpRWRtUVFSWHduVnFwcmNyRXBIQzUKWWJJenl4cVZRWWZIZVdWc2N0SUxFeVdPMFQwMS9tYkZ2RVY4QW9BL3phekIycjF3Y0VaeUNSRXFXbExrS2RXTwpNYmU1WnlwMDNhQzlBOSs4cThQNFBEOUNnVXlrcVovN0xydGlja2k0TVBsK2VmaGFlUk9YSEJMSURuQmplTHJkCmJGdHpaOVhKQWdNQkFBR2pWakJVTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQUZJNFFkU2FSRFVodi8wWjk0ZzV5RmlVdQppTGRwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBRjNvaTFvNVlNM1UvOWxPRElhaUpmaGllNzdieG1pN3NwCitiL0NEOGRCTXhIdWpPVnBSaTFNaHRJa2U3N2U1RVVuZEFGRzYvQTQwK3c2TGtCYXJFUEl5R2daRlBvZkttcSsKRGlIMGxPZHBYY0hFd3laTjhWSmdRd0JKUkhKcDhBc0p3TGFYWGplU1FQdmZyeHhLdUFGenRzeXNaYlBMUkxoYQpjeXZmeDNwTE91ZVJ4MDJqQVZUUlNJUGNPZEV4SERPa0FGWFFCdDV4TFo2eGFKTU1VQjZXNUYwcVpPelFuVUZsCk80QUNNOEhnOEdKc2xqLzFqZnpZaGlneWdwL2psQ0Jkd1Izb2c1ZXFqaC9ZRzlxWHVsU2Z0WUNhMURaOEp2QnAKaGRSYzZxOVM0ZFdtRW9zMmkxTDA1WUs3ZFBaQk5JVHRLNkVzQS9CRCs0VlVWRHczZldkNQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdUNrWmZpaUpld2lHbDZOdEcrd1BVUDRnd3BnczdtbHRmdTF3YTgxMjNiSCtnbVJ2CkZOWDEyTzFHTXNKcm1teFhyZ0pEN1FEbDYyRitHaXkzNW1PS0x4VjdFejJaRGtTaU1uUkVYSVAvMnZmanNNTDcKRlFVQ2NlR1hwaXBPMHJJVmRhL2NGM28xYXgwUURaNmdxeHdGeE9LZloyaVhiSFRLOWdJQXBCQi9qSmkwb3l5egpQMkZhNnRiMjRoSFprRUVWOEoxYXFhM0t4S1J3dVdHeU04c2FsVUdIeDNsbGJITFNDeE1sanRFOU5mNW14YnhGCmZBS0FQODJzd2RxOWNIQkdjZ2tSS2xwUzVDblZqakczdVdjcWROMmd2UVBmdkt2RCtEdy9Rb0ZNcEttZit5NjcKWW5KSXVERDVmbm40V25rVGx4d1N5QTV3WTNpNjNXeGJjMmZWeVFJREFRQUJBb0lCQUdYMWUwTzV0Y1FFU0dBVAovd2lDZlVoZUtrMFNhMjNqdU5lWkpiREpwSkhCUmlOeTczMGRxR3Rka292djBCdEMrSmhDY05ENnVsRERQVW5JCmtGaGhxOU85bE5KbVBDTUdKTGJDWUViSVhoTWhRMUpONFMwV0JQQi84Ykh4b29wTVJrMU4vQkNUZkplOUUzSTIKN01WUFVuSmE5ZDRPcmlkQjBreTVkeGxlZVAraGFvT2NTejJGamhXbDEycnlqbm1ad0draU1BdnhHazBaejFkZgpxZ0QyUE5CRHYzMTdtMkJxYjdkcENaTmZsSi90MGtqQ3hGbm81UmRsUUl5NDhSeml4LytaV1ZSeWlsaVFjL2srCnY4UzRTWGljZjBDK1RzV0orZzBNU3NoaGs1SWV4OURzTmR1bmJrSTcxMDNoR2ZOcGZYbGlRVlZHTlZ2eHdNd2kKenV4eE9nRUNnWUVBeGFYbU9GSUhkbm1tU0tMbEp4KzhBc3pPcmdTNDFKM2RMemtUZUZhWU5STHY0Z1AyK05SQwpQcXAzYVRCRmNjNWFMTDFXcG1ZRkdGSkthejRUbm05dlRKN0Nhell6K1RZRWc4OWlidGJESmhFSlFaSGtRaDQxCnJXRlBHTERWL1ZVSk5kaStSRk9TU2xMVDVGY2gvS1NkdFFRazhkbStUempiWi94a29ZOVpKbmtDZ1lFQTdvZlQKRnQ0MytQY0w5T0ZoTGZjYnB2SUJkeDdzWlAyK09NVzF3eG92TnhTbElSelBSQi9QbXNsai9hUzV2VnFWOVlVdgp0YjVFaUM3cUVYN2JVS0lZT1hMY1c3N3ozcmxpODdMRW5CWkNDemFhdHk3Nmw1U2lneU16VDY3MS9DdjNSSVJYCkw5citoQ2ZUUThPVHBUVDR6R3ZhRWJrQldBcnRmeVZOTDhkVGxkRUNnWUF1TE44SFEyckk3QXpFSllKaHpKRXgKR2tZaTg2bDJ5dGJVNUlHKytJUWd5aWJPNTl3NEwrYTJHejlBak8xOGRCZ3ZJYUR2eVIvaG1jQVhJKzZUY2pkUApjRHU5cm5FZ0JOV3pNYTB1ZGZBcm9ZbEhEMTJEY09sYmMwTjJZa0hzS0lTNVZzVEUwNzEycmJraFBKWU5IeXhWCkVQM01udkZPTXR0WGhPakJzZXJEQ1FLQmdEOCtBOW1zVVdyUkZYcDN4eXhJdUN3clBmZzNXclhzRU9NOGlGU1MKUExKOTVzcEF1VE4ydTdSdWNQUnZHRS84Rklaa0thSW1NRVZyS3VRNG5pMWl6TWx1aXI1SWdxQXF4dkdXRkVyTwpHL1NkSmFncjdJVUVBNUtCWXJsZHlocHlEYjA4MldEMnowUjZ5cWpNMGZpYmN0dkFQTEUyUEFUNzRMdzFSNkhEClY0WUJBb0dCQUo4bUdVNWJnejBQUnJZL1hoUERER2tIa0Z3SHVocHhjL2tieWNweVVTZ2NDR2dsczFXTTdKOFUKQ05MV2pQc0pnSTNxeTJSN0xxaGxmNHJRK1orLy80WHdyNW1NYWpqSEVGZE93Z2xqbFZRdEljNWM4a2U3MzM1SwprbCtxblpJbUcwRy83R1hUZHhSOW9mQXBBZVFkT3pJTFY0K1YzZm5DRGNYMnBHWjZOOTJUCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== | ||
- name: kubedelta | ||
user: | ||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJekNDQWd1Z0F3SUJBZ0lJZlJpZk1qZWl1eFV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBNE1qWXhNVEEwTlROYUZ3MHlOREF4TURJeE1ESXhNelZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzRLUmwrS0lsN0NJYVgKbzIwYjdBOVEvaURDbUN6dWFXMSs3WEJyelhiZHNmNkNaRzhVMWZYWTdVWXl3bXVhYkZldUFrUHRBT1hyWVg0YQpMTGZtWTRvdkZYc1RQWmtPUktJeWRFUmNnLy9hOStPd3d2c1ZCUUp4NFplbUtrN1NzaFYxcjl3WGVqVnJIUkFOCm5xQ3JIQVhFNHA5bmFKZHNkTXIyQWdDa0VIK01tTFNqTExNL1lWcnExdmJpRWRtUVFSWHduVnFwcmNyRXBIQzUKWWJJenl4cVZRWWZIZVdWc2N0SUxFeVdPMFQwMS9tYkZ2RVY4QW9BL3phekIycjF3Y0VaeUNSRXFXbExrS2RXTwpNYmU1WnlwMDNhQzlBOSs4cThQNFBEOUNnVXlrcVovN0xydGlja2k0TVBsK2VmaGFlUk9YSEJMSURuQmplTHJkCmJGdHpaOVhKQWdNQkFBR2pWakJVTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQUZJNFFkU2FSRFVodi8wWjk0ZzV5RmlVdQppTGRwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBRjNvaTFvNVlNM1UvOWxPRElhaUpmaGllNzdieG1pN3NwCitiL0NEOGRCTXhIdWpPVnBSaTFNaHRJa2U3N2U1RVVuZEFGRzYvQTQwK3c2TGtCYXJFUEl5R2daRlBvZkttcSsKRGlIMGxPZHBYY0hFd3laTjhWSmdRd0JKUkhKcDhBc0p3TGFYWGplU1FQdmZyeHhLdUFGenRzeXNaYlBMUkxoYQpjeXZmeDNwTE91ZVJ4MDJqQVZUUlNJUGNPZEV4SERPa0FGWFFCdDV4TFo2eGFKTU1VQjZXNUYwcVpPelFuVUZsCk80QUNNOEhnOEdKc2xqLzFqZnpZaGlneWdwL2psQ0Jkd1Izb2c1ZXFqaC9ZRzlxWHVsU2Z0WUNhMURaOEp2QnAKaGRSYzZxOVM0ZFdtRW9zMmkxTDA1WUs3ZFBaQk5JVHRLNkVzQS9CRCs0VlVWRHczZldkNQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== | ||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdUNrWmZpaUpld2lHbDZOdEcrd1BVUDRnd3BnczdtbHRmdTF3YTgxMjNiSCtnbVJ2CkZOWDEyTzFHTXNKcm1teFhyZ0pEN1FEbDYyRitHaXkzNW1PS0x4VjdFejJaRGtTaU1uUkVYSVAvMnZmanNNTDcKRlFVQ2NlR1hwaXBPMHJJVmRhL2NGM28xYXgwUURaNmdxeHdGeE9LZloyaVhiSFRLOWdJQXBCQi9qSmkwb3l5egpQMkZhNnRiMjRoSFprRUVWOEoxYXFhM0t4S1J3dVdHeU04c2FsVUdIeDNsbGJITFNDeE1sanRFOU5mNW14YnhGCmZBS0FQODJzd2RxOWNIQkdjZ2tSS2xwUzVDblZqakczdVdjcWROMmd2UVBmdkt2RCtEdy9Rb0ZNcEttZit5NjcKWW5KSXVERDVmbm40V25rVGx4d1N5QTV3WTNpNjNXeGJjMmZWeVFJREFRQUJBb0lCQUdYMWUwTzV0Y1FFU0dBVAovd2lDZlVoZUtrMFNhMjNqdU5lWkpiREpwSkhCUmlOeTczMGRxR3Rka292djBCdEMrSmhDY05ENnVsRERQVW5JCmtGaGhxOU85bE5KbVBDTUdKTGJDWUViSVhoTWhRMUpONFMwV0JQQi84Ykh4b29wTVJrMU4vQkNUZkplOUUzSTIKN01WUFVuSmE5ZDRPcmlkQjBreTVkeGxlZVAraGFvT2NTejJGamhXbDEycnlqbm1ad0draU1BdnhHazBaejFkZgpxZ0QyUE5CRHYzMTdtMkJxYjdkcENaTmZsSi90MGtqQ3hGbm81UmRsUUl5NDhSeml4LytaV1ZSeWlsaVFjL2srCnY4UzRTWGljZjBDK1RzV0orZzBNU3NoaGs1SWV4OURzTmR1bmJrSTcxMDNoR2ZOcGZYbGlRVlZHTlZ2eHdNd2kKenV4eE9nRUNnWUVBeGFYbU9GSUhkbm1tU0tMbEp4KzhBc3pPcmdTNDFKM2RMemtUZUZhWU5STHY0Z1AyK05SQwpQcXAzYVRCRmNjNWFMTDFXcG1ZRkdGSkthejRUbm05dlRKN0Nhell6K1RZRWc4OWlidGJESmhFSlFaSGtRaDQxCnJXRlBHTERWL1ZVSk5kaStSRk9TU2xMVDVGY2gvS1NkdFFRazhkbStUempiWi94a29ZOVpKbmtDZ1lFQTdvZlQKRnQ0MytQY0w5T0ZoTGZjYnB2SUJkeDdzWlAyK09NVzF3eG92TnhTbElSelBSQi9QbXNsai9hUzV2VnFWOVlVdgp0YjVFaUM3cUVYN2JVS0lZT1hMY1c3N3ozcmxpODdMRW5CWkNDemFhdHk3Nmw1U2lneU16VDY3MS9DdjNSSVJYCkw5citoQ2ZUUThPVHBUVDR6R3ZhRWJrQldBcnRmeVZOTDhkVGxkRUNnWUF1TE44SFEyckk3QXpFSllKaHpKRXgKR2tZaTg2bDJ5dGJVNUlHKytJUWd5aWJPNTl3NEwrYTJHejlBak8xOGRCZ3ZJYUR2eVIvaG1jQVhJKzZUY2pkUApjRHU5cm5FZ0JOV3pNYTB1ZGZBcm9ZbEhEMTJEY09sYmMwTjJZa0hzS0lTNVZzVEUwNzEycmJraFBKWU5IeXhWCkVQM01udkZPTXR0WGhPakJzZXJEQ1FLQmdEOCtBOW1zVVdyUkZYcDN4eXhJdUN3clBmZzNXclhzRU9NOGlGU1MKUExKOTVzcEF1VE4ydTdSdWNQUnZHRS84Rklaa0thSW1NRVZyS3VRNG5pMWl6TWx1aXI1SWdxQXF4dkdXRkVyTwpHL1NkSmFncjdJVUVBNUtCWXJsZHlocHlEYjA4MldEMnowUjZ5cWpNMGZpYmN0dkFQTEUyUEFUNzRMdzFSNkhEClY0WUJBb0dCQUo4bUdVNWJnejBQUnJZL1hoUERER2tIa0Z3SHVocHhjL2tieWNweVVTZ2NDR2dsczFXTTdKOFUKQ05MV2pQc0pnSTNxeTJSN0xxaGxmNHJRK1orLy80WHdyNW1NYWpqSEVGZE93Z2xqbFZRdEljNWM4a2U3MzM1SwprbCtxblpJbUcwRy83R1hUZHhSOW9mQXBBZVFkT3pJTFY0K1YzZm5DRGNYMnBHWjZOOTJUCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU, we have GetDefaultKubeConfigPath() in the config.go, we also have a way to retrive this from k8s client-go.