-
-
Save pgavlin/35c55c320d9904d04aa4674ff3c9a615 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go | |
index 621d693..6375fa0 100644 | |
--- a/pkg/provider/provider.go | |
+++ b/pkg/provider/provider.go | |
@@ -19,11 +19,13 @@ import ( | |
"context" | |
"encoding/json" | |
"fmt" | |
+ "net" | |
"net/url" | |
"os" | |
"reflect" | |
"strings" | |
"sync" | |
+ "syscall" | |
jsonpatch "github.com/evanphx/json-patch" | |
"github.com/golang/glog" | |
@@ -105,6 +107,8 @@ type kubeProvider struct { | |
enableDryRun bool | |
suppressDeprecationWarnings bool | |
enableSecrets bool | |
+ configError error | |
+ clusterUnreachable bool | |
config *rest.Config // Cluster config, e.g., through $KUBECONFIG file. | |
@@ -364,7 +368,12 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ | |
k.k8sVersion = cluster.GetServerVersion(cs.DiscoveryClientCached) | |
if _, err = k.getResources(); err != nil { | |
- return nil, fmt.Errorf("unable to load schema information from the API server: %v", err) | |
+ wrappedErr := fmt.Errorf("unable to load schema information from the API server: %v", err) | |
+ if !isClusterUnreachableError(err) { | |
+ return nil, wrappedErr | |
+ } | |
+ k.configError = wrappedErr | |
+ k.clusterUnreachable = true | |
} | |
return &pulumirpc.ConfigureResponse{ | |
@@ -694,6 +703,10 @@ func (k *kubeProvider) StreamInvoke( | |
// required for correctness, violations thereof can negatively impact the end-user experience, as | |
// the provider inputs are using for detecting and rendering diffs. | |
func (k *kubeProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
// | |
// Behavior as of v0.12.x: We take two inputs: | |
// | |
@@ -899,6 +912,10 @@ func (k *kubeProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) ( | |
func (k *kubeProvider) Diff( | |
ctx context.Context, req *pulumirpc.DiffRequest, | |
) (*pulumirpc.DiffResponse, error) { | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
// | |
// Behavior as of v0.12.x: We take 2 inputs: | |
// | |
@@ -1092,6 +1109,10 @@ func (k *kubeProvider) Diff( | |
func (k *kubeProvider) Create( | |
ctx context.Context, req *pulumirpc.CreateRequest, | |
) (*pulumirpc.CreateResponse, error) { | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
// | |
// Behavior as of v0.12.x: We take 1 input: | |
// | |
@@ -1231,6 +1252,15 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p | |
label := fmt.Sprintf("%s.Read(%s)", k.label(), urn) | |
glog.V(9).Infof("%s executing", label) | |
+ // If the cluster is unreachable, consider the resource deleted and inform the user. | |
+ if k.clusterUnreachable { | |
+ _ = k.host.Log(ctx, diag.Warning, urn, fmt.Sprintf("cluster unreachable: %v", k.configError)) | |
+ return deleteResponse, nil | |
+ } | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
// Obtain new properties, create a Kubernetes `unstructured.Unstructured` that we can pass to the | |
// validation routines. | |
oldState, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{ | |
@@ -1377,6 +1407,10 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p | |
func (k *kubeProvider) Update( | |
ctx context.Context, req *pulumirpc.UpdateRequest, | |
) (*pulumirpc.UpdateResponse, error) { | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
// | |
// Behavior as of v0.12.x: We take 2 inputs: | |
// | |
@@ -1546,6 +1580,10 @@ func (k *kubeProvider) Update( | |
func (k *kubeProvider) Delete( | |
ctx context.Context, req *pulumirpc.DeleteRequest, | |
) (*pbempty.Empty, error) { | |
+ if k.configError != nil { | |
+ return nil, k.configError | |
+ } | |
+ | |
urn := resource.URN(req.GetUrn()) | |
label := fmt.Sprintf("%s.Delete(%s)", k.label(), urn) | |
glog.V(9).Infof("%s executing", label) | |
@@ -2248,3 +2286,26 @@ func annotateSecrets(outs, ins resource.PropertyMap) { | |
} | |
} | |
} | |
+ | |
+func isClusterUnreachableError(err error) bool { | |
+ if urlErr, ok := err.(*url.Error); ok { | |
+ err = urlErr.Err | |
+ } | |
+ if opErr, ok := err.(*net.OpError); ok { | |
+ err = opErr.Err | |
+ } | |
+ if dnsErr, ok := err.(*net.DNSError); ok { | |
+ // Consider "host not found" errors as "cluster unreachable". | |
+ return dnsErr.IsNotFound | |
+ } | |
+ if osErr, ok := err.(*os.SyscallError); ok { | |
+ err = osErr.Err | |
+ } | |
+ if errno, ok := err.(syscall.Errno); ok { | |
+ switch errno { | |
+ case syscall.ECONNABORTED, syscall.ECONNREFUSED, syscall.ECONNRESET, syscall.EHOSTDOWN, syscall.EHOSTUNREACH: | |
+ return true | |
+ } | |
+ } | |
+ return false | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment