github.com/tilt-dev/tilt@v0.36.0/internal/k8s/minikube.go (about) 1 package k8s 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "fmt" 8 "os/exec" 9 "regexp" 10 "strings" 11 12 "github.com/pkg/errors" 13 ) 14 15 // This isn't perfect (because it won't unquote the value right) but 16 // it's good enough for 99% of cases. 17 var envMatcher = regexp.MustCompile(`export (\w+)="([^"]+)"`) 18 var versionMatcher = regexp.MustCompile(`^minikube version: v([0-9.]+)$`) 19 20 // Error messages if Minikube is running OK but docker-env is unsupported. 21 var dockerEnvUnsupportedMsgs = []string{ 22 "ENV_DRIVER_CONFLICT", 23 "ENV_MULTINODE_CONFLICT", 24 "ENV_DOCKER_UNAVAILABLE", 25 "The docker-env command is only compatible", 26 } 27 28 type MinikubeClient interface { 29 Version(ctx context.Context) (string, error) 30 DockerEnv(ctx context.Context) (map[string]string, bool, error) 31 NodeIP(ctx context.Context) (NodeIP, error) 32 } 33 34 type minikubeClient struct { 35 // The minikube client needs to know which minikube profile to talk to. 36 // 37 // When minikube creates a cluster, it sets the kubeconfig context name and 38 // the cluster nameto the name of the profile. 39 // 40 // The cluster name is better because users 41 // don't usually rename it. 42 context ClusterName 43 } 44 45 func ProvideMinikubeClient(context ClusterName) MinikubeClient { 46 return minikubeClient{context: context} 47 } 48 49 func (mc minikubeClient) cmd(ctx context.Context, args ...string) *exec.Cmd { 50 args = append([]string{"-p", string(mc.context)}, args...) 51 return exec.CommandContext(ctx, "minikube", args...) 52 } 53 54 func (mc minikubeClient) Version(ctx context.Context) (string, error) { 55 cmd := mc.cmd(ctx, "version") 56 output, err := cmd.Output() 57 if err != nil { 58 exitErr, isExitErr := err.(*exec.ExitError) 59 if isExitErr { 60 return "", fmt.Errorf("Could not read minikube version.\n%s", string(exitErr.Stderr)) 61 } 62 return "", errors.Wrap(err, "Could not read minikube version") 63 } 64 return minikubeVersionFromOutput(output) 65 66 } 67 68 func minikubeVersionFromOutput(output []byte) (string, error) { 69 scanner := bufio.NewScanner(bytes.NewBuffer(output)) 70 for scanner.Scan() { 71 line := scanner.Text() 72 73 match := versionMatcher.FindStringSubmatch(line) 74 if len(match) > 0 { 75 return match[1], nil 76 } 77 } 78 79 return "", fmt.Errorf("version not found in output:\n%s", string(output)) 80 } 81 82 // Returns: 83 // - A map of env variables for the minikube docker-env. 84 // - True if this minikube supports a docker-env, false otherwise 85 // - An error if minikube doesn't appear to be running. 86 func (mc minikubeClient) DockerEnv(ctx context.Context) (map[string]string, bool, error) { 87 cmd := mc.cmd(ctx, "docker-env", "--shell", "sh") 88 output, err := cmd.Output() 89 if err != nil { 90 exitErr, isExitErr := err.(*exec.ExitError) 91 if isExitErr { 92 stderr := string(exitErr.Stderr) 93 for _, msg := range dockerEnvUnsupportedMsgs { 94 if strings.Contains(stderr, msg) { 95 return nil, false, nil 96 } 97 } 98 99 return nil, false, fmt.Errorf("Could not read docker env from minikube.\n"+ 100 "Did you forget to run `minikube start`?\n%s", stderr) 101 } 102 return nil, false, errors.Wrap(err, "Could not read docker env from minikube") 103 } 104 return dockerEnvFromOutput(output), true, nil 105 } 106 107 func dockerEnvFromOutput(output []byte) map[string]string { 108 result := make(map[string]string) 109 scanner := bufio.NewScanner(bytes.NewBuffer(output)) 110 for scanner.Scan() { 111 line := scanner.Text() 112 113 match := envMatcher.FindStringSubmatch(line) 114 if len(match) > 0 { 115 result[match[1]] = match[2] 116 } 117 } 118 119 return result 120 } 121 122 func (mc minikubeClient) NodeIP(ctx context.Context) (NodeIP, error) { 123 cmd := mc.cmd(ctx, "ip") 124 out, err := cmd.Output() 125 if err != nil { 126 exitErr, isExitErr := err.(*exec.ExitError) 127 if isExitErr { 128 return "", errors.Wrapf(exitErr, "Could not read node IP from minikube.\n"+ 129 "Did you forget to run `minikube start`?\n%s", string(exitErr.Stderr)) 130 } 131 return "", errors.Wrapf(err, "Could not read node IP from minikube") 132 } 133 134 return NodeIP(strings.TrimSpace(string(out))), nil 135 }