minikube/pkg/minikube/mustload/mustload.go

170 lines
4.9 KiB
Go

/*
Copyright 2020 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.
*/
// mustload loads minikube clusters, exiting with user-friendly messages
package mustload
import (
"fmt"
"net"
"os"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/state"
"github.com/golang/glog"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil/kverify"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
)
type ClusterController struct {
Config *config.ClusterConfig
API libmachine.API
CPHost *host.Host
CPNode *config.Node
CPRunner command.Runner
DriverIP net.IP
}
// Partial is a cmd-friendly way to load a cluster which may or may not be running
func Partial(name string) (libmachine.API, *config.ClusterConfig) {
glog.Infof("Loading cluster: %s", name)
api, err := machine.NewAPIClient()
if err != nil {
exit.WithError("libmachine failed", err)
}
cc, err := config.Load(name)
if err != nil {
if config.IsNotExist(err) {
out.T(out.Shrug, `There is no local cluster named "{{.cluster}}"`, out.V{"cluster": name})
exitTip("start", name, exit.Data)
}
exit.WithError("Error getting cluster config", err)
}
return api, cc
}
// Running is a cmd-friendly way to load a running cluster
func Running(name string) ClusterController {
api, cc := Partial(name)
cp, err := config.PrimaryControlPlane(cc)
if err != nil {
exit.WithError("Unable to find control plane", err)
}
machineName := driver.MachineName(*cc, cp)
hs, err := machine.Status(api, machineName)
if err != nil {
exit.WithError("Unable to get machine status", err)
}
if hs == state.None.String() {
out.T(out.Shrug, `The control plane node "{{.name}}" does not exist.`, out.V{"name": cp.Name})
exitTip("start", name, exit.Unavailable)
}
if hs == state.Stopped.String() {
out.T(out.Shrug, `The control plane node must be running for this command`)
exitTip("start", name, exit.Unavailable)
}
if hs != state.Running.String() {
out.T(out.Shrug, `The control plane node is not running (state={{.state}})`, out.V{"name": cp.Name, "state": hs})
exitTip("start", name, exit.Unavailable)
}
host, err := machine.LoadHost(api, name)
if err != nil {
exit.WithError("Unable to load host", err)
}
cr, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Unable to get command runner", err)
}
ips, err := host.Driver.GetIP()
if err != nil {
exit.WithError("Unable to get driver IP", err)
}
if driver.IsKIC(host.DriverName) {
ips = oci.DefaultBindIPV4
}
ip := net.ParseIP(ips)
if ip == nil {
exit.WithCodeT(exit.Software, fmt.Sprintf("Unable to parse driver IP: %q", ips))
}
return ClusterController{
API: api,
Config: cc,
CPRunner: cr,
CPHost: host,
CPNode: &cp,
DriverIP: ip,
}
}
// Healthy is a cmd-friendly way to load a healthy cluster
func Healthy(name string) ClusterController {
co := Running(name)
as, err := kverify.APIServerStatus(co.CPRunner, net.ParseIP(co.CPNode.IP), co.CPNode.Port)
if err != nil {
out.T(out.FailureType, `Unable to get control plane status: {{.error}}`, out.V{"error": err})
exitTip("delete", name, exit.Unavailable)
}
if as == state.Paused {
out.T(out.Shrug, `The control plane for "{{.name}}" is paused!`, out.V{"name": name})
exitTip("unpause", name, exit.Unavailable)
}
if as != state.Running {
out.T(out.Shrug, `This control plane is not running! (state={{.state}})`, out.V{"state": as.String()})
out.T(out.Warning, `This is unusual - you may want to investigate using "{{.command}} logs"`, out.V{"command": minikubeCmd(name)})
exitTip("start", name, exit.Unavailable)
}
return co
}
// ExampleCmd Return a minikube command containing the current profile name
func ExampleCmd(cname string, action string) string {
if cname != constants.DefaultClusterName {
return fmt.Sprintf("minikube %s -p %s", action, cname)
}
return fmt.Sprintf("minikube %s", action)
}
// exitTip returns an action tip and exits
func exitTip(action string, profile string, code int) {
command := ExampleCmd(profile, action)
out.T(out.Workaround, "To fix this, run: {{.command}}", out.V{"command": command})
os.Exit(code)
}