Files
odo/pkg/logs/logs.go
Dharmit Shah e3b3b8eb53 Add odo logs (#5760)
* Add odo logs

* Nolint for random number generation

* Changes based on Philippe's PR review

* Add logs for `odo logs`

* Add nolint at the right place to fix unit tests

* Changes based on PR feedback

* Name the key in unstructured.Unstructured

* Name containers with same names as c, c1, c2

* Remove unused struct field

* Modify documentation to follow general pattern

* Undo the changes done in earlier commits

* odo logs help message is accurate

* Update docs/website/versioned_docs/version-3.0.0/command-reference/logs.md

Co-authored-by: Parthvi Vala <pvala@redhat.com>

* Fixes broken link rendering

* Correct the example used in odo logs doc

* Make container name clearer in odo logs output

* Wrap at 120 chars, not 80

* Fixes to the document after rebase mistake

Co-authored-by: Parthvi Vala <pvala@redhat.com>
2022-06-13 00:57:30 -04:00

109 lines
3.5 KiB
Go

package logs
import (
"io"
"k8s.io/apimachinery/pkg/runtime/schema"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/redhat-developer/odo/pkg/kclient"
odolabels "github.com/redhat-developer/odo/pkg/labels"
corev1 "k8s.io/api/core/v1"
)
type LogsClient struct {
kubernetesClient kclient.ClientInterface
}
func NewLogsClient(kubernetesClient kclient.ClientInterface) *LogsClient {
return &LogsClient{
kubernetesClient: kubernetesClient,
}
}
var _ Client = (*LogsClient)(nil)
func (o *LogsClient) DevModeLogs(componentName string, namespace string) ([]map[string]io.ReadCloser, error) {
// get all resources in the namespace which are running in Dev mode
selector := odolabels.Builder().WithComponentName(componentName).WithMode(odolabels.ComponentDevMode).Selector()
resources, err := o.kubernetesClient.GetAllResourcesFromSelector(selector, namespace)
if err != nil {
return nil, err
}
// get all pods in the namespace
podList, err := o.kubernetesClient.GetAllPodsInNamespace()
if err != nil {
return nil, err
}
// match pod ownerReference (if any) with resources running in Dev mode
var pods []corev1.Pod
for _, pod := range podList.Items {
for _, owner := range pod.GetOwnerReferences() {
match, err := o.matchOwnerReferenceWithResources(owner, resources)
if err != nil {
return nil, err
} else if match {
pods = append(pods, pod)
break // because we don't need to check other owner references of the pod anymore
}
}
}
// get all containers from the pods of interest
podContainersMap := map[string][]corev1.Container{}
for _, pod := range pods {
for _, container := range pod.Spec.Containers {
if _, ok := podContainersMap[pod.Name]; !ok {
podContainersMap[pod.Name] = []corev1.Container{container}
} else {
podContainersMap[pod.Name] = append(podContainersMap[pod.Name], container)
}
}
}
// get logs of all containers
logs := []map[string]io.ReadCloser{}
for pod, containers := range podContainersMap {
for _, container := range containers {
containerLogs, err := o.kubernetesClient.GetPodLogs(pod, container.Name, false)
if err != nil {
return nil, err
}
logs = append(logs, map[string]io.ReadCloser{container.Name: containerLogs})
}
}
return logs, nil
}
// matchOwnerReferenceWithResources recursively checks if the owner reference passed to it matches any of the resources
// This is useful when trying to find if a pod is owned by any of the ReplicaSet or Deployment in the cluster.
func (o *LogsClient) matchOwnerReferenceWithResources(owner metav1.OwnerReference, resources []unstructured.Unstructured) (bool, error) {
// first, check if ownerReference belongs to any of the resources
for _, resource := range resources {
if resource.GetUID() != "" && owner.UID != "" && resource.GetUID() == owner.UID {
return true, nil
}
}
// second, get the resource indicated by ownerReference and check its ownerReferences field
restMapping, err := o.kubernetesClient.GetRestMappingFromGVK(schema.FromAPIVersionAndKind(owner.APIVersion, owner.Kind))
if err != nil {
return false, err
}
resource, err := o.kubernetesClient.GetDynamicResource(restMapping.Resource, owner.Name)
if err != nil {
return false, err
}
ownerReferences := resource.GetOwnerReferences()
// recursively check if ownerReference matches any of the resources' UID
for _, ownerReference := range ownerReferences {
return o.matchOwnerReferenceWithResources(ownerReference, resources)
}
return false, nil
}