Added update-context and kubeconfig to status.
parent
baa724d70e
commit
577816311e
|
@ -27,14 +27,16 @@ import (
|
||||||
cmdUtil "k8s.io/minikube/cmd/util"
|
cmdUtil "k8s.io/minikube/cmd/util"
|
||||||
"k8s.io/minikube/pkg/minikube/cluster"
|
"k8s.io/minikube/pkg/minikube/cluster"
|
||||||
"k8s.io/minikube/pkg/minikube/constants"
|
"k8s.io/minikube/pkg/minikube/constants"
|
||||||
|
kcfg "k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||||
"k8s.io/minikube/pkg/minikube/machine"
|
"k8s.io/minikube/pkg/minikube/machine"
|
||||||
)
|
)
|
||||||
|
|
||||||
var statusFormat string
|
var statusFormat string
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
MinikubeStatus string
|
MinikubeStatus string
|
||||||
LocalkubeStatus string
|
LocalkubeStatus string
|
||||||
|
KubeconfigStatus string
|
||||||
}
|
}
|
||||||
|
|
||||||
// statusCmd represents the status command
|
// statusCmd represents the status command
|
||||||
|
@ -57,14 +59,32 @@ var statusCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
ls := state.None.String()
|
ls := state.None.String()
|
||||||
|
ks := state.None.String()
|
||||||
if ms == state.Running.String() {
|
if ms == state.Running.String() {
|
||||||
ls, err = cluster.GetLocalkubeStatus(api)
|
ls, err = cluster.GetLocalkubeStatus(api)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorln("Error localkube status:", err)
|
glog.Errorln("Error localkube status:", err)
|
||||||
cmdUtil.MaybeReportErrorAndExit(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)
|
tmpl, err := template.New("status").Parse(statusFormat)
|
||||||
if err != nil {
|
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.
|
// StartCluster starts a k8s cluster on the specified Host.
|
||||||
func StartCluster(api libmachine.API, kubernetesConfig KubernetesConfig) error {
|
func StartCluster(api libmachine.API, kubernetesConfig KubernetesConfig) error {
|
||||||
h, err := CheckIfApiExistsAndLoad(api)
|
h, err := CheckIfApiExistsAndLoad(api)
|
||||||
|
|
|
@ -90,7 +90,7 @@ const (
|
||||||
MinimumDiskSizeMB = 2000
|
MinimumDiskSizeMB = 2000
|
||||||
DefaultVMDriver = "virtualbox"
|
DefaultVMDriver = "virtualbox"
|
||||||
DefaultStatusFormat = "minikube: {{.MinikubeStatus}}\n" +
|
DefaultStatusFormat = "minikube: {{.MinikubeStatus}}\n" +
|
||||||
"localkube: {{.LocalkubeStatus}}\n"
|
"localkube: {{.LocalkubeStatus}}\n" + "kubectl: {{.KubeconfigStatus}}\n"
|
||||||
DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n"
|
DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n"
|
||||||
DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n"
|
DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n"
|
||||||
GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json"
|
GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json"
|
||||||
|
|
|
@ -17,9 +17,13 @@ limitations under the License.
|
||||||
package kubeconfig
|
package kubeconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
@ -27,6 +31,8 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/tools/clientcmd/api"
|
"k8s.io/client-go/tools/clientcmd/api"
|
||||||
"k8s.io/client-go/tools/clientcmd/api/latest"
|
"k8s.io/client-go/tools/clientcmd/api/latest"
|
||||||
|
cfg "k8s.io/minikube/pkg/minikube/config"
|
||||||
|
"k8s.io/minikube/pkg/minikube/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KubeConfigSetup struct {
|
type KubeConfigSetup struct {
|
||||||
|
@ -178,3 +184,68 @@ func decode(data []byte) (*api.Config, error) {
|
||||||
|
|
||||||
return config.(*api.Config), nil
|
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 (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -49,6 +50,50 @@ users:
|
||||||
client-key: /home/la-croix/apiserver.key
|
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) {
|
func TestSetupKubeConfig(t *testing.T) {
|
||||||
setupCfg := &KubeConfigSetup{
|
setupCfg := &KubeConfigSetup{
|
||||||
ClusterName: "test",
|
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) {
|
func TestEmptyConfig(t *testing.T) {
|
||||||
tmp := tempFile(t, []byte{})
|
tmp := tempFile(t, []byte{})
|
||||||
defer os.Remove(tmp)
|
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.
|
// tempFile creates a temporary with the provided bytes as its contents.
|
||||||
// The caller is responsible for deleting file after use.
|
// The caller is responsible for deleting file after use.
|
||||||
func tempFile(t *testing.T, data []byte) string {
|
func tempFile(t *testing.T, data []byte) string {
|
||||||
|
|
Loading…
Reference in New Issue