github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/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 context KubeContext 36 } 37 38 func ProvideMinikubeClient(context KubeContext) MinikubeClient { 39 return minikubeClient{context: context} 40 } 41 42 func (mc minikubeClient) cmd(ctx context.Context, args ...string) *exec.Cmd { 43 args = append([]string{"-p", string(mc.context)}, args...) 44 return exec.CommandContext(ctx, "minikube", args...) 45 } 46 47 func (mc minikubeClient) Version(ctx context.Context) (string, error) { 48 cmd := mc.cmd(ctx, "version") 49 output, err := cmd.Output() 50 if err != nil { 51 exitErr, isExitErr := err.(*exec.ExitError) 52 if isExitErr { 53 return "", fmt.Errorf("Could not read minikube version.\n%s", string(exitErr.Stderr)) 54 } 55 return "", errors.Wrap(err, "Could not read minikube version") 56 } 57 return minikubeVersionFromOutput(output) 58 59 } 60 61 func minikubeVersionFromOutput(output []byte) (string, error) { 62 scanner := bufio.NewScanner(bytes.NewBuffer(output)) 63 for scanner.Scan() { 64 line := scanner.Text() 65 66 match := versionMatcher.FindStringSubmatch(line) 67 if len(match) > 0 { 68 return match[1], nil 69 } 70 } 71 72 return "", fmt.Errorf("version not found in output:\n%s", string(output)) 73 } 74 75 // Returns: 76 // - A map of env variables for the minikube docker-env. 77 // - True if this minikube supports a docker-env, false otherwise 78 // - An error if minikube doesn't appear to be running. 79 func (mc minikubeClient) DockerEnv(ctx context.Context) (map[string]string, bool, error) { 80 cmd := mc.cmd(ctx, "docker-env", "--shell", "sh") 81 output, err := cmd.Output() 82 if err != nil { 83 exitErr, isExitErr := err.(*exec.ExitError) 84 if isExitErr { 85 stderr := string(exitErr.Stderr) 86 for _, msg := range dockerEnvUnsupportedMsgs { 87 if strings.Contains(stderr, msg) { 88 return nil, false, nil 89 } 90 } 91 92 return nil, false, fmt.Errorf("Could not read docker env from minikube.\n"+ 93 "Did you forget to run `minikube start`?\n%s", stderr) 94 } 95 return nil, false, errors.Wrap(err, "Could not read docker env from minikube") 96 } 97 return dockerEnvFromOutput(output), true, nil 98 } 99 100 func dockerEnvFromOutput(output []byte) map[string]string { 101 result := make(map[string]string) 102 scanner := bufio.NewScanner(bytes.NewBuffer(output)) 103 for scanner.Scan() { 104 line := scanner.Text() 105 106 match := envMatcher.FindStringSubmatch(line) 107 if len(match) > 0 { 108 result[match[1]] = match[2] 109 } 110 } 111 112 return result 113 } 114 115 func (mc minikubeClient) NodeIP(ctx context.Context) (NodeIP, error) { 116 cmd := mc.cmd(ctx, "ip") 117 out, err := cmd.Output() 118 if err != nil { 119 exitErr, isExitErr := err.(*exec.ExitError) 120 if isExitErr { 121 return "", errors.Wrapf(exitErr, "Could not read node IP from minikube.\n"+ 122 "Did you forget to run `minikube start`?\n%s", string(exitErr.Stderr)) 123 } 124 return "", errors.Wrapf(err, "Could not read node IP from minikube") 125 } 126 127 return NodeIP(strings.TrimSpace(string(out))), nil 128 }