1 package component
2
3 import (
4 "context"
5 "fmt"
6 "io"
7 "path/filepath"
8 "strings"
9
10 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
11 "github.com/devfile/api/v2/pkg/devfile"
12 "github.com/devfile/library/v2/pkg/devfile/parser"
13 "github.com/devfile/library/v2/pkg/devfile/parser/data"
14 routev1 "github.com/openshift/api/route/v1"
15 "k8s.io/apimachinery/pkg/runtime"
16 "k8s.io/klog"
17
18 "github.com/redhat-developer/odo/pkg/alizer"
19 "github.com/redhat-developer/odo/pkg/api"
20 "github.com/redhat-developer/odo/pkg/kclient"
21 odolabels "github.com/redhat-developer/odo/pkg/labels"
22 "github.com/redhat-developer/odo/pkg/odo/commonflags"
23 "github.com/redhat-developer/odo/pkg/platform"
24 "github.com/redhat-developer/odo/pkg/podman"
25 "github.com/redhat-developer/odo/pkg/util"
26
27 corev1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29 )
30
31 const (
32 NotAvailable = "Not available"
33 UnknownValue = "Unknown"
34 )
35
36
37
38 func GetComponentTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) string {
39 var componentType string
40 if metadata.ProjectType != "" {
41 componentType = metadata.ProjectType
42 } else if metadata.Language != "" {
43 componentType = metadata.Language
44 } else {
45 componentType = NotAvailable
46 }
47 return componentType
48 }
49
50
51
52 func GetComponentRuntimeFromDevfileMetadata(metadata devfile.DevfileMetadata) string {
53 if metadata.ProjectType != "" {
54 return metadata.ProjectType
55 }
56 return metadata.Language
57 }
58
59
60
61
62
63
64
65 func GatherName(contextDir string, devfileObj *parser.DevfileObj) (string, error) {
66 var name string
67 if devfileObj != nil {
68 name = devfileObj.GetMetadataName()
69 if name == "" || strings.TrimSpace(name) == "" {
70
71
72
73 alizerClient := alizer.Alizer{}
74 return alizerClient.DetectName(filepath.Dir(devfileObj.Ctx.GetAbsPath()))
75 }
76 } else {
77
78 baseDir, err := filepath.Abs(contextDir)
79 if err != nil {
80 return "", err
81 }
82 name = filepath.Base(baseDir)
83 }
84
85
86 s := util.GetDNS1123Name(name)
87 klog.V(3).Infof("name of component is %q, and sanitized name is %q", name, s)
88
89 return s, nil
90 }
91
92
93 func Log(platformClient platform.Client, componentName string, appName string, follow bool, command v1alpha2.Command) (io.ReadCloser, error) {
94
95 pod, err := platformClient.GetRunningPodFromSelector(odolabels.GetSelector(componentName, appName, odolabels.ComponentDevMode, false))
96 if err != nil {
97 return nil, fmt.Errorf("a running component %s doesn't exist on the cluster: %w", componentName, err)
98 }
99
100 containerName := command.Exec.Component
101
102 return platformClient.GetPodLogs(pod.Name, containerName, follow)
103 }
104
105
106
107
108
109
110
111 func ListAllClusterComponents(client kclient.ClientInterface, namespace string) ([]api.ComponentAbstract, error) {
112
113
114 resourceList, err := client.GetAllResourcesFromSelector("", namespace)
115 if err != nil {
116 return nil, fmt.Errorf("unable to list all dynamic resources required to find components: %w", err)
117 }
118
119 var components []api.ComponentAbstract
120
121 for _, resource := range resourceList {
122
123
124 if resource.GetKind() == "PackageManifest" {
125 continue
126 }
127
128 var labels, annotations map[string]string
129
130
131 if resource.GetLabels() != nil {
132 labels = resource.GetLabels()
133 }
134 if resource.GetAnnotations() != nil {
135 annotations = resource.GetAnnotations()
136 }
137
138
139
140
141 name := odolabels.GetComponentName(labels)
142 if name == "" {
143 continue
144 }
145
146
147 componentType, err := odolabels.GetProjectType(nil, annotations)
148 if err != nil || componentType == "" {
149 componentType = api.TypeUnknown
150 }
151
152
153
154
155
156
157 managedBy := odolabels.GetManagedBy(labels)
158 if managedBy == "" && name == "" {
159 continue
160 }
161
162 managedByVersion := odolabels.GetManagedByVersion(labels)
163
164
165 component := api.ComponentAbstract{
166 Name: name,
167 ManagedBy: managedBy,
168 Type: componentType,
169 ManagedByVersion: managedByVersion,
170
171 RunningOn: commonflags.PlatformCluster,
172 Platform: commonflags.PlatformCluster,
173 }
174 mode := odolabels.GetMode(labels)
175 componentFound := false
176 for v, otherCompo := range components {
177 if component.Name == otherCompo.Name {
178 componentFound = true
179 if mode != "" {
180 if components[v].RunningIn == nil {
181 components[v].RunningIn = api.NewRunningModes()
182 }
183 components[v].RunningIn.AddRunningMode(api.RunningMode(strings.ToLower(mode)))
184 }
185 if otherCompo.Type == api.TypeUnknown && component.Type != api.TypeUnknown {
186 components[v].Type = component.Type
187 }
188 if otherCompo.ManagedBy == api.TypeUnknown && component.ManagedBy != api.TypeUnknown {
189 components[v].ManagedBy = component.ManagedBy
190 }
191 }
192 }
193 if !componentFound {
194 if mode != "" {
195 if component.RunningIn == nil {
196 component.RunningIn = api.NewRunningModes()
197 }
198 component.RunningIn.AddRunningMode(api.RunningMode(strings.ToLower(mode)))
199 }
200 components = append(components, component)
201 }
202 }
203
204 return components, nil
205 }
206
207 func ListAllComponents(client kclient.ClientInterface, podmanClient podman.Client, namespace string, devObj *parser.DevfileObj, componentName string) ([]api.ComponentAbstract, string, error) {
208 var (
209 allComponents []api.ComponentAbstract
210 )
211
212 if client != nil {
213 clusterComponents, err := ListAllClusterComponents(client, namespace)
214 if err != nil {
215 return nil, "", err
216 }
217 allComponents = append(allComponents, clusterComponents...)
218 }
219
220
221 if podmanClient != nil {
222 podmanComponents, err := podmanClient.ListAllComponents()
223 if err != nil {
224 return nil, "", err
225 }
226 allComponents = append(allComponents, podmanComponents...)
227 }
228
229 localComponent := api.ComponentAbstract{
230 Name: componentName,
231 ManagedBy: "",
232 RunningIn: api.NewRunningModes(),
233 }
234 if devObj != nil {
235 localComponent.Type = GetComponentTypeFromDevfileMetadata(devObj.Data.GetMetadata())
236 }
237
238 componentInDevfile := ""
239 if localComponent.Name != "" {
240 if !Contains(localComponent, allComponents) {
241 allComponents = append(allComponents, localComponent)
242 }
243 componentInDevfile = localComponent.Name
244 }
245 return allComponents, componentInDevfile, nil
246 }
247
248 func getResourcesForComponent(
249 ctx context.Context,
250 client platform.Client,
251 name string,
252 namespace string,
253 ) ([]unstructured.Unstructured, error) {
254 selector := odolabels.GetNameSelector(name)
255 resourceList, err := client.GetAllResourcesFromSelector(selector, namespace)
256 if err != nil {
257 return nil, err
258 }
259 filteredList := []unstructured.Unstructured{}
260 for _, resource := range resourceList {
261
262 if resource.GetKind() == "PackageManifest" {
263 continue
264 }
265 filteredList = append(filteredList, resource)
266 }
267 return filteredList, nil
268 }
269
270
271
272 func GetRunningModes(ctx context.Context, kubeClient kclient.ClientInterface, podmanClient podman.Client, name string) (map[platform.Client]api.RunningModes, error) {
273 var hasErr bool
274 var ns string
275 listByPlatform := make(map[platform.Client][]unstructured.Unstructured)
276 if kubeClient != nil {
277 ns = kubeClient.GetCurrentNamespace()
278 list, err := getResourcesForComponent(ctx, kubeClient, name, ns)
279 if err != nil {
280 klog.V(4).Infof("error while listing cluster components: %v", err)
281 hasErr = true
282 } else if len(list) > 0 {
283 listByPlatform[kubeClient] = list
284 }
285 }
286
287 if podmanClient != nil {
288 ns = ""
289 list, err := getResourcesForComponent(ctx, podmanClient, name, ns)
290 if err != nil {
291 klog.V(4).Infof("error while listing Podman components: %v", err)
292 hasErr = true
293 } else if len(list) > 0 {
294 listByPlatform[podmanClient] = list
295 }
296 }
297
298 if hasErr {
299 return nil, nil
300 }
301
302 if len(listByPlatform) == 0 {
303 return nil, NewNoComponentFoundError(name, ns)
304 }
305
306 result := make(map[platform.Client]api.RunningModes)
307 for plt, list := range listByPlatform {
308 mapResult := api.NewRunningModes()
309 for _, resource := range list {
310 resourceLabels := resource.GetLabels()
311 mode := odolabels.GetMode(resourceLabels)
312 if mode != "" {
313 mapResult.AddRunningMode(api.RunningMode(strings.ToLower(mode)))
314 }
315 }
316 result[plt] = mapResult
317 }
318
319 return result, nil
320 }
321
322
323
324 func Contains(component api.ComponentAbstract, components []api.ComponentAbstract) bool {
325 for _, comp := range components {
326 if component.Name == comp.Name {
327 return true
328 }
329 }
330 return false
331 }
332
333
334 func GetDevfileInfo(ctx context.Context, kubeClient kclient.ClientInterface, podmanClient podman.Client, name string) (parser.DevfileObj, error) {
335 var ns string
336 listByPlatform := make(map[platform.Client][]unstructured.Unstructured)
337 if kubeClient != nil {
338 ns = kubeClient.GetCurrentNamespace()
339 list, err := getResourcesForComponent(ctx, kubeClient, name, ns)
340 if err != nil {
341 klog.V(4).Infof("error while listing cluster components: %v", err)
342 } else if len(list) > 0 {
343 listByPlatform[kubeClient] = list
344 }
345 }
346 if podmanClient != nil {
347 ns = ""
348 list, err := getResourcesForComponent(ctx, podmanClient, name, "")
349 if err != nil {
350 klog.V(4).Infof("error while listing Podman components: %v", err)
351 } else if len(list) > 0 {
352 listByPlatform[podmanClient] = list
353 }
354 }
355
356 if len(listByPlatform) == 0 {
357 return parser.DevfileObj{}, NewNoComponentFoundError(name, ns)
358 }
359
360
361
362 kList := listByPlatform[kubeClient]
363 pList := listByPlatform[podmanClient]
364 if len(kList) > 0 {
365 err := checkLabelsForDevfileInfo(kList, pList)
366 if err != nil {
367 return parser.DevfileObj{}, err
368 }
369 }
370 if len(pList) > 0 {
371 err := checkLabelsForDevfileInfo(pList, kList)
372 if err != nil {
373 return parser.DevfileObj{}, err
374 }
375 }
376
377 if len(kList) > 0 {
378 return getDevfileInfoFromList(kList)
379 }
380 return getDevfileInfoFromList(pList)
381 }
382
383 func checkLabelsForDevfileInfo(l1 []unstructured.Unstructured, l2 []unstructured.Unstructured) error {
384 for _, k := range l1 {
385 var found bool
386 var (
387 kLabels = k.GetLabels()
388 kAnnotations = k.GetAnnotations()
389 kName = odolabels.GetComponentName(kLabels)
390 )
391 kProjectType, err := odolabels.GetProjectType(kLabels, kAnnotations)
392 if err != nil {
393 klog.V(7).Infof("error while working on cluster resource %q: %v", kName, err)
394 continue
395 }
396
397 var (
398 pName string
399 pProjectType string
400 )
401 for _, p := range l2 {
402 pLabels := p.GetLabels()
403 pAnnotations := p.GetAnnotations()
404 pName = odolabels.GetComponentName(pLabels)
405 pProjectType, err = odolabels.GetProjectType(pLabels, pAnnotations)
406 if err != nil {
407 klog.V(7).Infof("error while working on resource %q: %v", pName, err)
408 continue
409 }
410 if kName == pName {
411 found = true
412 break
413 }
414 }
415 if found {
416
417 if kProjectType != pProjectType {
418 return fmt.Errorf("found resource %q on both platforms, but with different project types: %q vs %q",
419 kName, kProjectType, pProjectType)
420 }
421 }
422 }
423 return nil
424 }
425
426 func getDevfileInfoFromList(list []unstructured.Unstructured) (parser.DevfileObj, error) {
427 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
428 if err != nil {
429 return parser.DevfileObj{}, err
430 }
431 metadata := devfileData.GetMetadata()
432 metadata.Name = UnknownValue
433 metadata.DisplayName = UnknownValue
434 metadata.ProjectType = UnknownValue
435 metadata.Language = UnknownValue
436 metadata.Version = UnknownValue
437 metadata.Description = UnknownValue
438
439 for _, resource := range list {
440 labels := resource.GetLabels()
441 annotations := resource.GetAnnotations()
442 name := odolabels.GetComponentName(labels)
443 if len(name) > 0 && metadata.Name == UnknownValue {
444 metadata.Name = name
445 }
446 typ, err := odolabels.GetProjectType(labels, annotations)
447 if err != nil {
448 continue
449 }
450 if len(typ) > 0 && metadata.ProjectType == UnknownValue {
451 metadata.ProjectType = typ
452 }
453 }
454 devfileData.SetMetadata(metadata)
455 return parser.DevfileObj{
456 Data: devfileData,
457 }, nil
458 }
459
460
461
462
463 func ListRoutesAndIngresses(client kclient.ClientInterface, componentName, appName string) (ings []api.ConnectionData, routes []api.ConnectionData, err error) {
464 if client == nil {
465 return nil, nil, nil
466 }
467
468 selector := odolabels.GetNameSelector(componentName)
469
470 k8sIngresses, err := client.ListIngresses(client.GetCurrentNamespace(), selector)
471 if err != nil {
472 return nil, nil, err
473 }
474 for _, ing := range k8sIngresses.Items {
475 if ownerReferences := ing.GetOwnerReferences(); ownerReferences != nil {
476 klog.V(4).Infof("Skipping Ingress %q created/owned by another resource: %v", ing.GetName(), ownerReferences)
477 continue
478 }
479 ings = append(ings, api.ConnectionData{
480 Name: ing.GetName(),
481 Rules: func() (rules []api.Rules) {
482 for _, rule := range ing.Spec.Rules {
483 var paths []string
484 for _, path := range rule.HTTP.Paths {
485 paths = append(paths, path.Path)
486 }
487 host := rule.Host
488 if host == "" {
489 host = "*"
490 }
491 rules = append(rules, api.Rules{Host: host, Paths: paths})
492 }
493 if len(ing.Spec.Rules) == 0 {
494 rules = append(rules, api.Rules{Host: "*", Paths: []string{"/*"}})
495 }
496 return rules
497 }(),
498 })
499 }
500
501 if isOC, e := client.IsProjectSupported(); !isOC {
502 if e != nil {
503 klog.V(4).Infof("unable to detect project support: %s", e.Error())
504 }
505 return ings, nil, nil
506 }
507
508 routeGVR, err := client.GetGVRFromGVK(kclient.RouteGVK)
509 if err != nil {
510 return nil, nil, fmt.Errorf("unable to determine GVR for %s: %w", kclient.RouteGVK.String(), err)
511 }
512
513 ocRoutes, err := client.ListDynamicResources(client.GetCurrentNamespace(), routeGVR, selector)
514 if err != nil {
515 return nil, nil, err
516 }
517 for _, u := range ocRoutes.Items {
518 if ownerReferences := u.GetOwnerReferences(); ownerReferences != nil {
519 klog.V(4).Infof("Skipping Route %q created/owned by another resource: %v", u.GetName(), ownerReferences)
520 continue
521 }
522 route := routev1.Route{}
523 err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &route)
524 if err != nil {
525 return nil, nil, err
526 }
527 routes = append(routes, api.ConnectionData{
528 Name: route.GetName(),
529 Rules: []api.Rules{
530 {Host: route.Spec.Host, Paths: []string{route.Spec.Path}},
531 },
532 })
533 }
534
535 return ings, routes, nil
536 }
537
538 func GetContainersNames(pod *corev1.Pod) []string {
539 result := make([]string, 0, len(pod.Spec.Containers))
540 for _, container := range pod.Spec.Containers {
541 result = append(result, container.Name)
542 }
543 return result
544 }
545
View as plain text