github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/utils/clientgo/resource.go (about) 1 package clientgo 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/caos/orbos/mntr" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 11 "k8s.io/apimachinery/pkg/runtime/schema" 12 "k8s.io/client-go/discovery" 13 "k8s.io/client-go/dynamic" 14 ) 15 16 var ( 17 ignoredResources = []string{ 18 "componentstatuses", 19 "endpoints", 20 "bindings", 21 "nodes", 22 "nodes", 23 "replicationcontrollers", 24 "podtemplates", 25 "limitranges", 26 "apiservices", 27 "controllerrevisions", 28 "leases", 29 "backendconfigs", 30 "updateinfos", 31 "runtimeclasses", 32 "storagestates", 33 "storageversionmigrations", 34 "csidrivers", 35 "csinodes", 36 "localsubjectaccessreviews", 37 "selfsubjectaccessreviews", 38 "selfsubjectrulesreviews", 39 "subjectaccessreviews", 40 "tokenreviews", 41 "scalingpolicies", 42 "priorityclasses", 43 } 44 ignoredGroupResources = []string{ 45 // "metrics.k8s.io/pods", 46 "metrics.k8s.io/nodes", 47 "networking.gke.io/managedcertificates", 48 "networking.gke.io/ingresses", 49 "networking.gke.io/networkpolicies", 50 } 51 ) 52 53 var ( 54 Limit int64 = 50 55 ) 56 57 type ResourceInfo struct { 58 Group string 59 Version string 60 Resource string 61 Namespaced bool 62 } 63 64 type Resource struct { 65 Group string 66 Version string 67 Resource string 68 Kind string 69 Name string 70 Namespace string 71 Labels map[string]string 72 } 73 74 type ResourceSorter []*Resource 75 76 func (a ResourceSorter) Len() int { return len(a) } 77 func (a ResourceSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 78 func (a ResourceSorter) Less(i, j int) bool { 79 return (a[i].Group < a[j].Group || 80 (a[i].Group == a[j].Group && a[i].Version < a[j].Version) || 81 (a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource < a[j].Resource) || 82 (a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind < a[j].Kind) || 83 (a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind == a[j].Kind && a[i].Name < a[j].Name) || 84 (a[i].Group == a[j].Group && a[i].Version == a[j].Version && a[i].Resource == a[j].Resource && a[i].Kind == a[j].Kind && a[i].Name == a[j].Name && a[i].Namespace < a[j].Namespace)) 85 } 86 87 func GetResource(monitor mntr.Monitor, group, version, resource, namespace, name string) (*Resource, error) { 88 res := schema.GroupVersionResource{Group: group, Version: version, Resource: resource} 89 90 conf, err := GetClusterConfig(monitor, "") 91 if err != nil { 92 return nil, err 93 } 94 95 clientset, err := dynamic.NewForConfig(conf) 96 if err != nil { 97 return nil, err 98 } 99 100 result, err := clientset.Resource(res).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{}) 101 if err != nil { 102 return nil, err 103 } 104 105 return &Resource{ 106 Kind: result.GetKind(), 107 Name: result.GetName(), 108 Namespace: result.GetNamespace(), 109 Labels: result.GetLabels(), 110 }, nil 111 } 112 113 func DeleteResource(monitor mntr.Monitor, resource *Resource) error { 114 res := schema.GroupVersionResource{Group: resource.Group, Version: resource.Version, Resource: resource.Resource} 115 conf, err := GetClusterConfig(monitor, "") 116 if err != nil { 117 return err 118 } 119 120 client, err := dynamic.NewForConfig(conf) 121 if err != nil { 122 return err 123 } 124 125 deletePolicy := metav1.DeletePropagationForeground 126 deleteOptions := &metav1.DeleteOptions{ 127 PropagationPolicy: &deletePolicy, 128 } 129 130 clientRes := client.Resource(res) 131 if resource.Namespace != "" { 132 err = clientRes.Namespace(resource.Namespace).Delete(context.Background(), resource.Name, *deleteOptions) 133 } else { 134 err = clientRes.Delete(context.Background(), resource.Name, *deleteOptions) 135 } 136 137 if err != nil { 138 return fmt.Errorf("error while deleting %s: %w", resource.Name, err) 139 } 140 return nil 141 } 142 143 func GetGroupVersionsResources(monitor mntr.Monitor, filtersResources []string) ([]*ResourceInfo, error) { 144 listMonitor := monitor.WithFields(map[string]interface{}{ 145 "action": "groupVersionResources", 146 }) 147 148 conf, err := GetClusterConfig(monitor, "") 149 if err != nil { 150 return nil, fmt.Errorf("getting cluster config failed: %w", err) 151 } 152 153 client, err := discovery.NewDiscoveryClientForConfig(conf) 154 if err != nil { 155 return nil, fmt.Errorf("creating discovery client failed: %w", err) 156 } 157 158 apiGroups, err := client.ServerGroups() 159 if err != nil { 160 return nil, fmt.Errorf("getting supported groups and versions failed: %w", err) 161 } 162 resourceInfoList := make([]*ResourceInfo, 0) 163 for _, apiGroup := range apiGroups.Groups { 164 version := apiGroup.PreferredVersion 165 apiResources, err := client.ServerResourcesForGroupVersion(version.GroupVersion) 166 if err != nil { 167 return nil, fmt.Errorf("getting supported resources failed for %s: %w", version.GroupVersion, err) 168 } 169 170 for _, apiResource := range apiResources.APIResources { 171 172 if filtersResources != nil && 173 len(filtersResources) > 0 && 174 containsFilter(filtersResources, apiGroup.Name, version.Version, apiResource.Kind) { 175 continue 176 } 177 178 resourceInfo := &ResourceInfo{ 179 Group: apiGroup.Name, 180 Version: version.Version, 181 Resource: apiResource.Name, 182 Namespaced: apiResource.Namespaced, 183 } 184 185 groupResource := strings.Join([]string{resourceInfo.Group, resourceInfo.Resource}, "/") 186 if !contains(ignoredResources, resourceInfo.Resource) && 187 !contains(ignoredGroupResources, groupResource) { 188 resourceInfoList = append(resourceInfoList, resourceInfo) 189 } 190 } 191 } 192 listMonitor.WithField("count", len(resourceInfoList)).Debug("Listed groupVersionsResources") 193 return resourceInfoList, nil 194 } 195 196 func contains(s []string, e string) bool { 197 for _, a := range s { 198 if a == e { 199 return true 200 } 201 } 202 return false 203 } 204 205 func ListResources(monitor mntr.Monitor, resourceInfoList []*ResourceInfo, labels map[string]string) ([]*Resource, error) { 206 listMonitor := monitor.WithFields(map[string]interface{}{ 207 "action": "listResources", 208 }) 209 conf, err := GetClusterConfig(monitor, "") 210 if err != nil { 211 return nil, err 212 } 213 214 client, err := dynamic.NewForConfig(conf) 215 if err != nil { 216 return nil, err 217 } 218 219 labelSelector := "" 220 for k, v := range labels { 221 if labelSelector == "" { 222 labelSelector = strings.Join([]string{k, v}, "=") 223 } else { 224 keyValue := strings.Join([]string{k, v}, "=") 225 labelSelector = strings.Join([]string{labelSelector, keyValue}, ", ") 226 } 227 } 228 229 listMonitor.WithFields(map[string]interface{}{ 230 "labelSelector": labelSelector, 231 }).Debug(fmt.Sprintf("Used labels")) 232 233 resourceList := make([]*Resource, 0) 234 for _, resourceInfo := range resourceInfoList { 235 236 gvr := schema.GroupVersionResource{Group: resourceInfo.Group, Version: resourceInfo.Version, Resource: resourceInfo.Resource} 237 238 listOpt := metav1.ListOptions{ 239 LabelSelector: labelSelector, 240 Limit: Limit, 241 } 242 list, err := client.Resource(gvr).List(context.Background(), listOpt) 243 if err != nil { 244 continue 245 } 246 247 resList, err := getResourcesFromList(resourceInfo.Group, resourceInfo.Version, resourceInfo.Resource, list.Items) 248 if err != nil { 249 return nil, err 250 } 251 252 for list.GetContinue() != "" { 253 listOpt.Continue = list.GetContinue() 254 listInternal, err := client.Resource(gvr).List(context.Background(), listOpt) 255 if err != nil { 256 continue 257 } 258 list = listInternal 259 260 resListContinue, err := getResourcesFromList(resourceInfo.Group, resourceInfo.Version, resourceInfo.Resource, list.Items) 261 if err != nil { 262 return nil, err 263 } 264 resList = append(resList, resListContinue...) 265 } 266 267 listMonitor.WithFields(map[string]interface{}{ 268 "group": resourceInfo.Group, 269 "version": resourceInfo.Version, 270 "resource": resourceInfo.Resource, 271 "count": len(resList), 272 }).Debug("Listed resources") 273 resourceList = append(resourceList, resList...) 274 } 275 276 listMonitor.WithFields(map[string]interface{}{ 277 "count": len(resourceList), 278 }).Info("All current resources") 279 return resourceList, nil 280 } 281 282 func getResourcesFromList(group, version, resource string, list []unstructured.Unstructured) ([]*Resource, error) { 283 284 resourceList := make([]*Resource, 0) 285 for _, item := range list { 286 287 name, found, err := unstructured.NestedString(item.Object, "metadata", "name") 288 if err != nil || !found { 289 return nil, err 290 } 291 292 kind, _, err := unstructured.NestedString(item.Object, "kind") 293 if err != nil { 294 return nil, err 295 } 296 297 namespace, _, err := unstructured.NestedString(item.Object, "metadata", "namespace") 298 if err != nil { 299 return nil, err 300 } 301 302 labels, _, err := unstructured.NestedMap(item.Object, "metadata", "labels") 303 if err != nil { 304 return nil, err 305 } 306 307 _, found, err = unstructured.NestedSlice(item.Object, "metadata", "ownerReferences") 308 if err != nil { 309 return nil, err 310 } 311 if found == true { 312 continue 313 } 314 315 labelStrs := make(map[string]string) 316 for k, label := range labels { 317 labelStrs[k] = label.(string) 318 } 319 320 resourceList = append(resourceList, &Resource{ 321 Group: group, 322 Version: version, 323 Resource: resource, 324 Name: name, 325 Kind: kind, 326 Namespace: namespace, 327 Labels: labelStrs, 328 }) 329 } 330 331 return resourceList, nil 332 } 333 334 func GetFilter(group, version, kind string) string { 335 return strings.Join([]string{group, version, kind}, "/") 336 } 337 338 func containsFilter(filters []string, group, version, kind string) bool { 339 compFilter := GetFilter(group, version, kind) 340 for _, filter := range filters { 341 if filter == compFilter { 342 return true 343 } 344 } 345 return false 346 }