Skip to content
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

Try to reload config when disconnected from the cluster #6130

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/kclient/interface.go
Expand Up @@ -76,6 +76,7 @@ type ClientInterface interface {
SetDiscoveryInterface(client discovery.DiscoveryInterface)
IsResourceSupported(apiGroup, apiVersion, resourceName string) (bool, error)
IsSSASupported() bool
Refresh() (newConfig bool, err error)

// namespace.go
GetCurrentNamespace() string
Expand Down
15 changes: 15 additions & 0 deletions pkg/kclient/mock_Client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions pkg/kclient/refresh.go
@@ -0,0 +1,67 @@
package kclient

import (
"fmt"
"reflect"

"k8s.io/client-go/tools/clientcmd"
)

// Refresh re-creates a new Kubernetes client and checks if the Config changes
// If config changed, updates the Kubernetes client with the new configuration and returns true
// If the namespace or cluster of the current context has changed since the last time
// the config has been loaded, the function will not update the configuration
func (c *Client) Refresh() (bool, error) {
newClient, err := New()
if err != nil {
return false, err
}

oldCluster, oldNs, err := getContext(c)
if err != nil {
return false, err
}
newCluster, newNs, err := getContext(newClient)
if err != nil {
return false, err
}

if oldCluster != newCluster {
return false, fmt.Errorf("cluster changed (%q -> %q), won't refresh the configuration", oldCluster, newCluster)
}
if oldNs != newNs {
return false, fmt.Errorf("namespace changed (%q -> %q), won't refresh the configuration", oldNs, newNs)
}

updated, err := isConfigUpdated(c.GetConfig(), newClient.GetConfig())
if err != nil {
return false, err
}

if updated {
*c = *newClient
}
return updated, nil
}

func getContext(c *Client) (cluster string, namespace string, err error) {
raw, err := c.GetConfig().RawConfig()
if err != nil {
return "", "", err
}

currentCtx := raw.Contexts[raw.CurrentContext]
return currentCtx.Cluster, currentCtx.Namespace, nil
}

func isConfigUpdated(oldC, newC clientcmd.ClientConfig) (bool, error) {
oldRaw, err := oldC.RawConfig()
if err != nil {
return false, err
}
newRaw, err := newC.RawConfig()
if err != nil {
return false, err
}
return !reflect.DeepEqual(oldRaw, newRaw), nil
}
32 changes: 24 additions & 8 deletions pkg/watch/watch.go
Expand Up @@ -27,6 +27,7 @@ import (

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/klog"
Expand Down Expand Up @@ -175,7 +176,7 @@ func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ct
}

o.keyWatcher = getKeyWatcher(ctx, out)
return o.eventWatcher(ctx, parameters, out, evaluateFileChanges, processEvents, componentStatus)
return o.eventWatcher(ctx, parameters, out, evaluateFileChanges, o.processEvents, componentStatus)
}

// eventWatcher loops till the context's Done channel indicates it to stop looping, at which point it performs cleanup.
Expand Down Expand Up @@ -424,7 +425,7 @@ func evaluateFileChanges(events []fsnotify.Event, path string, fileIgnores []str
return changedFiles, deletedPaths
}

func processEvents(
func (o *WatchClient) processEvents(
changedFiles, deletedPaths []string,
parameters WatchParameters,
out io.Writer,
Expand Down Expand Up @@ -461,12 +462,23 @@ func processEvents(
return nil, err
}
klog.V(4).Infof("Error from Push: %v", err)
if parameters.WatchFiles {
// Log and output, but intentionally not exiting on error here.
// We don't want to break watch when push failed, it might be fixed with the next change.
fmt.Fprintf(out, "%s - %s\n\n", PushErrorString, err.Error())
// Log and output, but intentionally not exiting on error here.
// We don't want to break watch when push failed, it might be fixed with the next push.
if kerrors.IsUnauthorized(err) || kerrors.IsForbidden(err) {
fmt.Fprintf(out, "Error connecting to the cluster. Please log in again\n\n")
var refreshed bool
refreshed, err = o.kubeClient.Refresh()
if err != nil {
fmt.Fprintf(out, "Error updating Kubernetes config: %s\n", err)
} else if refreshed {
fmt.Fprintf(out, "Updated Kubernetes config\n")
}
} else {
return nil, err
if parameters.WatchFiles {
fmt.Fprintf(out, "%s - %s\n\n", PushErrorString, err.Error())
} else {
return nil, err
}
}
wait := backoff.Delay()
return &wait, nil
Expand All @@ -484,7 +496,11 @@ func (o *WatchClient) CleanupDevResources(devfileObj parser.DevfileObj, componen
fmt.Fprintln(out, "Cleaning resources, please wait")
isInnerLoopDeployed, resources, err := o.deleteClient.ListResourcesToDeleteFromDevfile(devfileObj, "app", componentName, labels.ComponentDevMode)
if err != nil {
fmt.Fprintf(out, "failed to delete inner loop resources: %v", err)
if kerrors.IsUnauthorized(err) || kerrors.IsForbidden(err) {
fmt.Fprintf(out, "Error connecting to the cluster, the resources were not cleaned up.\nPlease log in again and cleanup the resource with `odo delete component`\n\n")
} else {
rm3l marked this conversation as resolved.
Show resolved Hide resolved
fmt.Fprintf(out, "Failed to delete inner loop resources: %v\n", err)
}
return err
}
// if innerloop deployment resource is present, then execute preStop events
Expand Down