github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/builtin.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provider 5 6 import ( 7 "bytes" 8 "fmt" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 14 jujuclock "github.com/juju/clock" 15 "github.com/juju/errors" 16 "github.com/juju/utils/v3" 17 "github.com/juju/utils/v3/exec" 18 19 k8s "github.com/juju/juju/caas/kubernetes" 20 "github.com/juju/juju/caas/kubernetes/clientconfig" 21 k8scloud "github.com/juju/juju/caas/kubernetes/cloud" 22 jujucloud "github.com/juju/juju/cloud" 23 envtools "github.com/juju/juju/environs/tools" 24 "github.com/juju/juju/version" 25 ) 26 27 func attemptMicroK8sCloud(cmdRunner CommandRunner, getKubeConfigDir func() (string, error)) (jujucloud.Cloud, error) { 28 microk8sConfig, err := getLocalMicroK8sConfig(cmdRunner, getKubeConfigDir) 29 if err != nil { 30 return jujucloud.Cloud{}, err 31 } 32 33 k8sCloud, err := k8scloud.CloudFromKubeConfigClusterReader( 34 k8s.MicroK8sClusterName, 35 bytes.NewReader(microk8sConfig), 36 k8scloud.CloudParamaters{ 37 Description: jujucloud.DefaultCloudDescription(jujucloud.CloudTypeKubernetes), 38 Name: k8s.K8sCloudMicrok8s, 39 Regions: []jujucloud.Region{{ 40 Name: k8s.Microk8sRegion, 41 }}, 42 }, 43 ) 44 45 return k8sCloud, err 46 } 47 48 func attemptMicroK8sCredential(cmdRunner CommandRunner, getKubeConfigDir func() (string, error)) (jujucloud.Credential, error) { 49 microk8sConfig, err := getLocalMicroK8sConfig(cmdRunner, getKubeConfigDir) 50 if err != nil { 51 return jujucloud.Credential{}, err 52 } 53 54 k8sConfig, err := k8scloud.ConfigFromReader(bytes.NewReader(microk8sConfig)) 55 if err != nil { 56 return jujucloud.Credential{}, errors.Annotate(err, "processing microk8s config to make juju credentials") 57 } 58 59 contextName, err := k8scloud.PickContextByClusterName(k8sConfig, k8s.MicroK8sClusterName) 60 if err != nil { 61 return jujucloud.Credential{}, errors.Trace(err) 62 } 63 64 context := k8sConfig.Contexts[contextName] 65 resolver := clientconfig.GetJujuAdminServiceAccountResolver(jujuclock.WallClock) 66 conf, err := resolver(k8s.K8sCloudMicrok8s, k8sConfig, contextName) 67 if err != nil { 68 return jujucloud.Credential{}, errors.Annotate(err, "resolving microk8s credentials") 69 } 70 71 return k8scloud.CredentialFromKubeConfig(context.AuthInfo, conf) 72 } 73 74 // For testing. 75 var CheckJujuOfficial = envtools.JujudVersion 76 77 func decideKubeConfigDir() (string, error) { 78 jujuDir, err := envtools.ExistingJujuLocation() 79 if err != nil { 80 return "", errors.Annotate(err, "cannot find juju binary") 81 } 82 _, isOffical, err := CheckJujuOfficial(jujuDir) 83 if err != nil && !errors.IsNotFound(err) { 84 return "", errors.Trace(err) 85 } 86 if isOffical { 87 return filepath.Join(os.Getenv("SNAP_DATA"), "microk8s", "credentials", "client.config"), nil 88 } 89 return filepath.Join("/var/snap/microk8s/current/", "credentials", "client.config"), nil 90 } 91 92 var microk8sGroupError = ` 93 Insufficient permissions to access MicroK8s. 94 You can either try again with sudo or add the user %s to the 'snap_microk8s' group: 95 96 sudo usermod -a -G snap_microk8s %s 97 98 After this, reload the user groups either via a reboot or by running 'newgrp snap_microk8s'. 99 `[1:] 100 101 func getLocalMicroK8sConfig(cmdRunner CommandRunner, getKubeConfigDir func() (string, error)) ([]byte, error) { 102 if runtime.GOOS != "linux" { 103 return getLocalMicroK8sConfigNonLinux(cmdRunner) 104 } 105 106 notSupportErr := errors.NewNotSupported(nil, fmt.Sprintf("juju %q can only work with strictly confined microk8s", version.Current)) 107 clientConfigPath, err := getKubeConfigDir() 108 if err != nil { 109 return nil, errors.Trace(err) 110 } 111 logger.Tracef("reading kubeconfig %q", clientConfigPath) 112 content, err := os.ReadFile(clientConfigPath) 113 if os.IsNotExist(err) { 114 return nil, errors.Annotatef(notSupportErr, "%q does not exist", clientConfigPath) 115 } 116 if os.IsPermission(err) { 117 user, err := utils.LocalUsername() 118 if err != nil { 119 user = "<user>" 120 } 121 return nil, errors.Errorf(microk8sGroupError, user, user) 122 } 123 if err != nil { 124 return nil, errors.Annotatef(err, "cannot read %q", clientConfigPath) 125 } 126 return content, nil 127 } 128 129 func getLocalMicroK8sConfigNonLinux(cmdRunner CommandRunner) ([]byte, error) { 130 _, err := cmdRunner.LookPath("microk8s") 131 if err != nil { 132 return []byte{}, errors.NotFoundf("microk8s") 133 } 134 execParams := exec.RunParams{ 135 Commands: "microk8s config", 136 } 137 result, err := cmdRunner.RunCommands(execParams) 138 if err != nil { 139 return []byte{}, err 140 } 141 if result.Code != 0 { 142 // TODO - confined snaps can't execute other commands. 143 errMessage := strings.ReplaceAll(string(result.Stderr), "\n", "") 144 if strings.HasSuffix(strings.ToLower(errMessage), "permission denied") { 145 return []byte{}, errors.NotFoundf("microk8s") 146 } 147 return []byte{}, errors.New(string(result.Stderr)) 148 } else { 149 if strings.HasPrefix(strings.ToLower(string(result.Stdout)), "microk8s is not running") { 150 return []byte{}, errors.NotFoundf("microk8s is not running") 151 } 152 } 153 return result.Stdout, nil 154 }