Skip to content

Commit

Permalink
Improving UX for kops validate cluster
Browse files Browse the repository at this point in the history
Before the `kops validate cluster` attempts to connect to the K8s API
endpoint, the code now checks to see if the API DNS Entry is the kops
placeholder IP Address 203.0.113.123.  It prints a message to the user
and err's.  There is a new init func in validate cluster that disables
CGO based DNS for Darwin OS.  Darwin does two things with kops
validates; it caches the IP address, and it does not return the
placeholder IP address.  We cannot use CGO base DNS with kops validate.
  • Loading branch information
chrislovecnm committed Nov 16, 2017
1 parent 2b2b597 commit c7ef856
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
28 changes: 28 additions & 0 deletions cmd/kops/validate_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"io"
"os"
"runtime"
"strings"

"github.com/golang/glog"
Expand All @@ -31,10 +32,18 @@ import (
"k8s.io/kops/cmd/kops/util"
api "k8s.io/kops/pkg/apis/kops"
apiutil "k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/validation"
"k8s.io/kops/util/pkg/tables"
)

func init() {
if runtime.GOOS == "darwin" {
// In order for net.LookupHost(apiAddr.Host) to lookup our placeholder address on darwin, we have to
os.Setenv("GODEBUG", "netdns=go")
}
}

type ValidateClusterOptions struct {
// No options yet
}
Expand Down Expand Up @@ -105,6 +114,25 @@ func RunValidateCluster(f *util.Factory, cmd *cobra.Command, args []string, out
return fmt.Errorf("Cannot build kube api client for %q: %v\n", contextName, err)
}

// Do not use if we are running gossip
if !dns.IsGossipHostname(cluster.ObjectMeta.Name) {
hasPlaceHolderIPAddress, err := validation.HasPlaceHolderIP(contextName)
if err != nil {
return err
}

if hasPlaceHolderIPAddress {
fmt.Println(
"Validation Failed\n\n" +
"The dns-controller Kubernetes deployment has not updated the Kubernetes cluster's API DNS entry to the correct IP address." +
" The API DNS IP address is the placeholder address that kops creates: 203.0.113.123." +
" Please wait about 5-10 minutes for a master to start, dns-controller to launch, and DNS to propagate." +
" The protokube container and dns-controller deployment logs may contain more diagnostic information." +
" Etcd and the API DNS entries must be updated for a kops Kubernetes cluster to start.")
return fmt.Errorf("\nCannot reach cluster's API server: unable to Validate Cluster: %s", cluster.ObjectMeta.Name)
}
}

validationCluster, validationFailed := validation.ValidateCluster(cluster.ObjectMeta.Name, list, k8sClient)

if validationCluster == nil || validationCluster.NodeList == nil || validationCluster.NodeList.Items == nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/validation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
],
)

Expand Down
30 changes: 30 additions & 0 deletions pkg/validation/validate_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ package validation

import (
"fmt"
"net/url"
"time"

"net"

"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/upup/pkg/fi"
Expand Down Expand Up @@ -54,6 +58,32 @@ type ValidationNode struct {
Status v1.ConditionStatus `json:"status,omitempty"`
}

// HasPlaceHolderIP checks if the API DNS has been updated
func HasPlaceHolderIP(clusterName string) (bool, error) {

config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{CurrentContext: clusterName}).ClientConfig()

apiAddr, err := url.Parse(config.Host)
if err != nil {
return true, fmt.Errorf("unable to parse Kubernetes cluster API URL: %v", err)
}

hostAddrs, err := net.LookupHost(apiAddr.Host)
if err != nil {
return true, fmt.Errorf("unable to resolve Kubernetes cluster API URL dns: %v", err)
}

for _, h := range hostAddrs {
if h == "203.0.113.123" {
return true, nil
}
}

return false, nil
}

// ValidateCluster validate a k8s cluster with a provided instance group list
func ValidateCluster(clusterName string, instanceGroupList *kops.InstanceGroupList, clusterKubernetesClient kubernetes.Interface) (*ValidationCluster, error) {
var instanceGroups []*kops.InstanceGroup
Expand Down

0 comments on commit c7ef856

Please sign in to comment.