Added update-context and kubeconfig to status.
parent
baa724d70e
commit
577816311e
|
@ -27,14 +27,16 @@ import (
|
|||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
kcfg "k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
)
|
||||
|
||||
var statusFormat string
|
||||
|
||||
type Status struct {
|
||||
MinikubeStatus string
|
||||
LocalkubeStatus string
|
||||
MinikubeStatus string
|
||||
LocalkubeStatus string
|
||||
KubeconfigStatus string
|
||||
}
|
||||
|
||||
// statusCmd represents the status command
|
||||
|
@ -57,14 +59,32 @@ var statusCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
ls := state.None.String()
|
||||
ks := state.None.String()
|
||||
if ms == state.Running.String() {
|
||||
ls, err = cluster.GetLocalkubeStatus(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error localkube status:", err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
ip, err := cluster.GetHostDriverIP(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error host driver ip status:", err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
kstatus, err := kcfg.GetKubeConfigStatus(ip, constants.KubeconfigPath)
|
||||
if err != nil {
|
||||
glog.Errorln("Error kubeconfig status:", err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if kstatus {
|
||||
ks = "Correctly Configured: pointing to minikube-vm at " + ip.String()
|
||||
} else {
|
||||
ks = "Misconfigured: pointing to stale minikube-vm." +
|
||||
"\nTo fix the kubectl context, run minikube update-context"
|
||||
}
|
||||
}
|
||||
status := Status{ms, ls}
|
||||
|
||||
status := Status{ms, ls, ks}
|
||||
|
||||
tmpl, err := template.New("status").Parse(statusFormat)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
kcfg "k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
)
|
||||
|
||||
// updateContextCmd represents the update-context command
|
||||
var updateContextCmd = &cobra.Command{
|
||||
Use: "update-context",
|
||||
Short: "Verify the IP address of the running cluster in kubeconfig.",
|
||||
Long: `Retrieves the IP address of the running cluster, checks it
|
||||
with IP in kubeconfig, and corrects kubeconfig if incorrect.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
api, err := machine.NewAPIClient(clientType)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting client: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer api.Close()
|
||||
ip, err := cluster.GetHostDriverIP(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error host driver ip status:", err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
kstatus, err := kcfg.UpdateKubeconfigIP(ip, constants.KubeconfigPath)
|
||||
if err != nil {
|
||||
glog.Errorln("Error kubeconfig status:", err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if kstatus {
|
||||
fmt.Println("Reconfigured kubeconfig IP, now pointing at " + ip.String())
|
||||
} else {
|
||||
fmt.Println("Kubeconfig IP correctly configured, pointing at " + ip.String())
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(updateContextCmd)
|
||||
}
|
|
@ -165,6 +165,24 @@ func GetLocalkubeStatus(api libmachine.API) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// GetHostDriverIP gets the ip address of the current minikube cluster
|
||||
func GetHostDriverIP(api libmachine.API) (net.IP, error) {
|
||||
host, err := CheckIfApiExistsAndLoad(api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipStr, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error getting IP")
|
||||
}
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return nil, errors.Wrap(err, "Error parsing IP")
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// StartCluster starts a k8s cluster on the specified Host.
|
||||
func StartCluster(api libmachine.API, kubernetesConfig KubernetesConfig) error {
|
||||
h, err := CheckIfApiExistsAndLoad(api)
|
||||
|
|
|
@ -90,7 +90,7 @@ const (
|
|||
MinimumDiskSizeMB = 2000
|
||||
DefaultVMDriver = "virtualbox"
|
||||
DefaultStatusFormat = "minikube: {{.MinikubeStatus}}\n" +
|
||||
"localkube: {{.LocalkubeStatus}}\n"
|
||||
"localkube: {{.LocalkubeStatus}}\n" + "kubectl: {{.KubeconfigStatus}}\n"
|
||||
DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n"
|
||||
DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n"
|
||||
GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json"
|
||||
|
|
|
@ -17,9 +17,13 @@ limitations under the License.
|
|||
package kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
@ -27,6 +31,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/client-go/tools/clientcmd/api/latest"
|
||||
cfg "k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
)
|
||||
|
||||
type KubeConfigSetup struct {
|
||||
|
@ -178,3 +184,68 @@ func decode(data []byte) (*api.Config, error) {
|
|||
|
||||
return config.(*api.Config), nil
|
||||
}
|
||||
|
||||
// GetKubeConfigStatus verifys the ip stored in kubeconfig.
|
||||
func GetKubeConfigStatus(ip net.IP, filename string) (bool, error) {
|
||||
if ip == nil {
|
||||
return false, fmt.Errorf("Error, empty ip passed")
|
||||
}
|
||||
kip, err := getIPFromKubeConfig(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if kip.Equal(ip) {
|
||||
return true, nil
|
||||
}
|
||||
// Kubeconfig IP misconfigured
|
||||
return false, nil
|
||||
|
||||
}
|
||||
|
||||
// UpdateKubeconfigIP overwrites the IP stored in kubeconfig with the provided IP.
|
||||
func UpdateKubeconfigIP(ip net.IP, filename string) (bool, error) {
|
||||
if ip == nil {
|
||||
return false, fmt.Errorf("Error, empty ip passed")
|
||||
}
|
||||
kip, err := getIPFromKubeConfig(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if kip.Equal(ip) {
|
||||
return false, nil
|
||||
}
|
||||
con, err := ReadConfigOrNew(filename)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "Error getting kubeconfig status")
|
||||
}
|
||||
// Safe to lookup server because if field non-existent getIPFromKubeconfig would have given an error
|
||||
con.Clusters[cfg.GetMachineName()].Server = "https://" + ip.String() + ":" + strconv.Itoa(constants.APIServerPort)
|
||||
err = WriteConfig(con, filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Kubeconfig IP reconfigured
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// getIPFromKubeConfig returns the IP address stored for minikube in the kubeconfig specified
|
||||
func getIPFromKubeConfig(filename string) (net.IP, error) {
|
||||
con, err := ReadConfigOrNew(filename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error getting kubeconfig status")
|
||||
}
|
||||
cluster, ok := con.Clusters[cfg.GetMachineName()]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Kubeconfig does not have a record of the machine cluster")
|
||||
}
|
||||
kurl, err := url.Parse(cluster.Server)
|
||||
if err != nil {
|
||||
return net.ParseIP(cluster.Server), nil
|
||||
}
|
||||
kip, _, err := net.SplitHostPort(kurl.Host)
|
||||
if err != nil {
|
||||
return net.ParseIP(kurl.Host), nil
|
||||
}
|
||||
ip := net.ParseIP(kip)
|
||||
return ip, nil
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package kubeconfig
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
@ -49,6 +50,50 @@ users:
|
|||
client-key: /home/la-croix/apiserver.key
|
||||
`)
|
||||
|
||||
var fakeKubeCfg2 = []byte(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority: /home/la-croix/apiserver.crt
|
||||
server: https://192.168.10.100:8443
|
||||
name: minikube
|
||||
contexts:
|
||||
- context:
|
||||
cluster: la-croix
|
||||
user: la-croix
|
||||
name: la-croix
|
||||
current-context: la-croix
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: la-croix
|
||||
user:
|
||||
client-certificate: /home/la-croix/apiserver.crt
|
||||
client-key: /home/la-croix/apiserver.key
|
||||
`)
|
||||
|
||||
var fakeKubeCfg3 = []byte(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority: /home/la-croix/apiserver.crt
|
||||
server: https://192.168.1.1:8443
|
||||
name: minikube
|
||||
contexts:
|
||||
- context:
|
||||
cluster: la-croix
|
||||
user: la-croix
|
||||
name: la-croix
|
||||
current-context: la-croix
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: la-croix
|
||||
user:
|
||||
client-certificate: /home/la-croix/apiserver.crt
|
||||
client-key: /home/la-croix/apiserver.key
|
||||
`)
|
||||
|
||||
func TestSetupKubeConfig(t *testing.T) {
|
||||
setupCfg := &KubeConfigSetup{
|
||||
ClusterName: "test",
|
||||
|
@ -128,6 +173,116 @@ func TestSetupKubeConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetKubeConfigStatus(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
description string
|
||||
ip net.IP
|
||||
existing []byte
|
||||
err bool
|
||||
status bool
|
||||
}{
|
||||
{
|
||||
description: "empty ip",
|
||||
ip: nil,
|
||||
existing: fakeKubeCfg,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
description: "no minikube cluster",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
description: "exactly matching ip",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg2,
|
||||
status: true,
|
||||
},
|
||||
{
|
||||
description: "different ips",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
configFilename := tempFile(t, test.existing)
|
||||
statusActual, err := GetKubeConfigStatus(test.ip, configFilename)
|
||||
if err != nil && !test.err {
|
||||
t.Errorf("Got unexpected error: %s", err)
|
||||
}
|
||||
if err == nil && test.err {
|
||||
t.Errorf("Expected error but got none: %s", err)
|
||||
}
|
||||
if test.status != statusActual {
|
||||
t.Errorf("Expected status %t, but got %t", test.status, statusActual)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKubeconfigIP(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
description string
|
||||
ip net.IP
|
||||
existing []byte
|
||||
err bool
|
||||
status bool
|
||||
expCfg []byte
|
||||
}{
|
||||
{
|
||||
description: "empty ip",
|
||||
ip: nil,
|
||||
existing: fakeKubeCfg2,
|
||||
err: true,
|
||||
expCfg: fakeKubeCfg2,
|
||||
},
|
||||
{
|
||||
description: "no minikube cluster",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg,
|
||||
err: true,
|
||||
expCfg: fakeKubeCfg,
|
||||
},
|
||||
{
|
||||
description: "same IP",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg2,
|
||||
expCfg: fakeKubeCfg2,
|
||||
},
|
||||
{
|
||||
description: "different IP",
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
existing: fakeKubeCfg3,
|
||||
status: true,
|
||||
expCfg: fakeKubeCfg2,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
configFilename := tempFile(t, test.existing)
|
||||
statusActual, err := UpdateKubeconfigIP(test.ip, configFilename)
|
||||
if err != nil && !test.err {
|
||||
t.Errorf("Got unexpected error: %s", err)
|
||||
}
|
||||
if err == nil && test.err {
|
||||
t.Errorf("Expected error but got none: %s", err)
|
||||
}
|
||||
if test.status != statusActual {
|
||||
t.Errorf("Expected status %t, but got %t", test.status, statusActual)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyConfig(t *testing.T) {
|
||||
tmp := tempFile(t, []byte{})
|
||||
defer os.Remove(tmp)
|
||||
|
@ -178,6 +333,42 @@ func TestNewConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetIPFromKubeConfig(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
description string
|
||||
cfg []byte
|
||||
ip net.IP
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
description: "normal IP",
|
||||
cfg: fakeKubeCfg2,
|
||||
ip: net.ParseIP("192.168.10.100"),
|
||||
},
|
||||
{
|
||||
description: "no minikube cluster",
|
||||
cfg: fakeKubeCfg,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
configFilename := tempFile(t, test.cfg)
|
||||
ip, err := getIPFromKubeConfig(configFilename)
|
||||
if err != nil && !test.err {
|
||||
t.Errorf("Got unexpected error: %s", err)
|
||||
}
|
||||
if err == nil && test.err {
|
||||
t.Errorf("Expected error but got none: %s", err)
|
||||
}
|
||||
if !ip.Equal(test.ip) {
|
||||
t.Errorf("IP returned: %s does not match ip given: %s", ip, test.ip)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// tempFile creates a temporary with the provided bytes as its contents.
|
||||
// The caller is responsible for deleting file after use.
|
||||
func tempFile(t *testing.T, data []byte) string {
|
||||
|
|
Loading…
Reference in New Issue