github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/k8s/info.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package k8s provides a client for interacting with a Kubernetes cluster. 5 package k8s 6 7 import ( 8 "errors" 9 "fmt" 10 "regexp" 11 12 "strings" 13 ) 14 15 // List of supported distros via distro detection. 16 const ( 17 DistroIsUnknown = "unknown" 18 DistroIsK3s = "k3s" 19 DistroIsK3d = "k3d" 20 DistroIsKind = "kind" 21 DistroIsMicroK8s = "microk8s" 22 DistroIsEKS = "eks" 23 DistroIsEKSAnywhere = "eksanywhere" 24 DistroIsDockerDesktop = "dockerdesktop" 25 DistroIsGKE = "gke" 26 DistroIsAKS = "aks" 27 DistroIsRKE2 = "rke2" 28 DistroIsTKG = "tkg" 29 ) 30 31 // DetectDistro returns the matching distro or unknown if not found. 32 func (k *K8s) DetectDistro() (string, error) { 33 kindNodeRegex := regexp.MustCompile(`^kind://`) 34 k3dNodeRegex := regexp.MustCompile(`^k3s://k3d-`) 35 eksNodeRegex := regexp.MustCompile(`^aws:///`) 36 gkeNodeRegex := regexp.MustCompile(`^gce://`) 37 aksNodeRegex := regexp.MustCompile(`^azure:///subscriptions`) 38 rke2Regex := regexp.MustCompile(`^rancher/rancher-agent:v2`) 39 tkgRegex := regexp.MustCompile(`^projects\.registry\.vmware\.com/tkg/tanzu_core/`) 40 41 nodes, err := k.GetNodes() 42 if err != nil { 43 return DistroIsUnknown, errors.New("error getting cluster nodes") 44 } 45 46 // All nodes should be the same for what we are looking for 47 node := nodes.Items[0] 48 49 // Regex explanation: https://regex101.com/r/TIUQVe/1 50 // https://github.com/rancher/k3d/blob/v5.2.2/cmd/node/nodeCreate.go#L187 51 if k3dNodeRegex.MatchString(node.Spec.ProviderID) { 52 return DistroIsK3d, nil 53 } 54 55 // Regex explanation: https://regex101.com/r/le7PRB/1 56 // https://github.com/kubernetes-sigs/kind/pull/1805 57 if kindNodeRegex.MatchString(node.Spec.ProviderID) { 58 return DistroIsKind, nil 59 } 60 61 // https://github.com/kubernetes/cloud-provider-aws/blob/454ed784c33b974c873c7d762f9d30e7c4caf935/pkg/providers/v2/instances.go#L234 62 if eksNodeRegex.MatchString(node.Spec.ProviderID) { 63 return DistroIsEKS, nil 64 } 65 66 if gkeNodeRegex.MatchString(node.Spec.ProviderID) { 67 return DistroIsGKE, nil 68 } 69 70 // https://github.com/kubernetes/kubernetes/blob/v1.23.4/staging/src/k8s.io/legacy-cloud-providers/azure/azure_wrap.go#L46 71 if aksNodeRegex.MatchString(node.Spec.ProviderID) { 72 return DistroIsAKS, nil 73 } 74 75 labels := node.GetLabels() 76 for _, label := range labels { 77 // kubectl get nodes --selector node.kubernetes.io/instance-type=k3s for K3s 78 if label == "node.kubernetes.io/instance-type=k3s" { 79 return DistroIsK3s, nil 80 } 81 // kubectl get nodes --selector microk8s.io/cluster=true for MicroK8s 82 if label == "microk8s.io/cluster=true" { 83 return DistroIsMicroK8s, nil 84 } 85 } 86 87 if node.GetName() == "docker-desktop" { 88 return DistroIsDockerDesktop, nil 89 } 90 91 for _, images := range node.Status.Images { 92 for _, image := range images.Names { 93 if rke2Regex.MatchString(image) { 94 return DistroIsRKE2, nil 95 } 96 if tkgRegex.MatchString(image) { 97 return DistroIsTKG, nil 98 } 99 } 100 } 101 102 namespaces, err := k.GetNamespaces() 103 if err != nil { 104 return DistroIsUnknown, errors.New("error getting namespace list") 105 } 106 107 // kubectl get ns eksa-system for EKS Anywhere 108 for _, namespace := range namespaces.Items { 109 if namespace.Name == "eksa-system" { 110 return DistroIsEKSAnywhere, nil 111 } 112 } 113 114 return DistroIsUnknown, nil 115 } 116 117 // GetArchitectures returns the cluster system architectures if found. 118 func (k *K8s) GetArchitectures() ([]string, error) { 119 nodes, err := k.GetNodes() 120 if err != nil { 121 return nil, err 122 } 123 124 if len(nodes.Items) == 0 { 125 return nil, errors.New("could not identify node architecture") 126 } 127 128 archMap := map[string]bool{} 129 130 for _, node := range nodes.Items { 131 archMap[node.Status.NodeInfo.Architecture] = true 132 } 133 134 architectures := []string{} 135 136 for arch := range archMap { 137 architectures = append(architectures, arch) 138 } 139 140 return architectures, nil 141 } 142 143 // GetServerVersion retrieves and returns the k8s revision. 144 func (k *K8s) GetServerVersion() (version string, err error) { 145 versionInfo, err := k.Clientset.Discovery().ServerVersion() 146 if err != nil { 147 return "", fmt.Errorf("unable to get Kubernetes version from the cluster : %w", err) 148 } 149 150 return versionInfo.String(), nil 151 } 152 153 // MakeLabels is a helper to format a map of label key and value pairs into a single string for use as a selector. 154 func MakeLabels(labels map[string]string) string { 155 var out []string 156 for key, value := range labels { 157 out = append(out, fmt.Sprintf("%s=%s", key, value)) 158 } 159 return strings.Join(out, ",") 160 }