...

Source file src/github.com/redhat-developer/odo/pkg/podman/pods.go

Documentation: github.com/redhat-developer/odo/pkg/podman

     1  package podman
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"os/exec"
    10  	"strings"
    11  	"time"
    12  
    13  	corev1 "k8s.io/api/core/v1"
    14  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    15  	"k8s.io/apimachinery/pkg/watch"
    16  	"k8s.io/klog"
    17  
    18  	"github.com/redhat-developer/odo/pkg/platform"
    19  )
    20  
    21  // GetPodsMatchingSelector returns all pods matching the given label selector.
    22  func (o *PodmanCli) GetPodsMatchingSelector(selector string) (*corev1.PodList, error) {
    23  	podsReport, err := o.getPodsFromSelector(selector)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	var result corev1.PodList
    29  	for _, podReport := range podsReport {
    30  		pod, err := o.KubeGenerate(podReport.Name)
    31  		if err != nil {
    32  			// The pod has disappeared in the meantime, forget it
    33  			continue
    34  		}
    35  		// We remove the podname- prefix from the container names as Podman adds this prefix
    36  		// (to avoid colliding container names?)
    37  		for c := range pod.Spec.Containers {
    38  			container := &pod.Spec.Containers[c]
    39  			prefix := pod.GetName() + "-"
    40  			container.Name = strings.TrimPrefix(container.Name, prefix)
    41  		}
    42  		inspect, err := o.PodInspect(podReport.Name)
    43  		if err != nil {
    44  			// The pod has disappeared in the meantime, forget it
    45  			continue
    46  		}
    47  		pod.Status.Phase = corev1.PodPhase(inspect.State)
    48  
    49  		result.Items = append(result.Items, *pod)
    50  	}
    51  	return &result, nil
    52  }
    53  
    54  // GetAllResourcesFromSelector returns all resources of any kind matching the given label selector.
    55  func (o *PodmanCli) GetAllResourcesFromSelector(selector string, _ string) ([]unstructured.Unstructured, error) {
    56  	list, err := o.getPodsFromSelector(selector)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	for _, pod := range list {
    61  		klog.V(5).Infof("\npod name: %s", pod.Name)
    62  		klog.V(5).Infof("labels:")
    63  		for k, v := range pod.Labels {
    64  			klog.V(5).Infof(" - %s: %s", k, v)
    65  		}
    66  	}
    67  
    68  	var result []unstructured.Unstructured
    69  	for _, pod := range list {
    70  		u := unstructured.Unstructured{}
    71  		u.SetName(pod.Name)
    72  		u.SetLabels(pod.Labels)
    73  		result = append(result, u)
    74  	}
    75  
    76  	return result, nil
    77  }
    78  
    79  // GetAllPodsInNamespaceMatchingSelector returns all pods matching the given label selector and in the specified namespace.
    80  func (o *PodmanCli) GetAllPodsInNamespaceMatchingSelector(selector string, ns string) (*corev1.PodList, error) {
    81  	// In podman, we return the pods, as there is no resource containing PodSpec
    82  	return o.GetPodsMatchingSelector(selector)
    83  }
    84  
    85  // GetRunningPodFromSelector returns any pod matching the given label selector.
    86  // If multiple pods are found, implementations might have different behavior, by either returning an error or returning any element.
    87  func (o *PodmanCli) GetRunningPodFromSelector(selector string) (*corev1.Pod, error) {
    88  	list, err := o.getPodsFromSelector(selector)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	numPods := len(list)
    93  	if numPods == 0 {
    94  		return nil, &platform.PodNotFoundError{Selector: selector}
    95  	} else if numPods > 1 {
    96  		return nil, fmt.Errorf("multiple Pods exist for the selector: %v. Only one must be present", selector)
    97  	}
    98  
    99  	podReport := list[0]
   100  	var pod corev1.Pod
   101  	pod.SetName(podReport.Name)
   102  	pod.SetLabels(podReport.Labels)
   103  
   104  	inspect, err := o.PodInspect(podReport.Name)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	if inspect.State != "Running" {
   109  		return nil, fmt.Errorf("a pod exists but is not in Running state. Current status=%v", inspect.State)
   110  	}
   111  
   112  	for _, container := range podReport.Containers {
   113  		// Names of users containers are prefixed with pod name by podman
   114  		if !strings.HasPrefix(container.Names, podReport.Name+"-") {
   115  			continue
   116  		}
   117  		pod.Spec.Containers = append(pod.Spec.Containers, corev1.Container{
   118  			Name: strings.TrimPrefix(container.Names, podReport.Name+"-"),
   119  		})
   120  	}
   121  	return &pod, nil
   122  }
   123  
   124  func (o *PodmanCli) getPodsFromSelector(selector string) ([]ListPodsReport, error) {
   125  	args := []string{"pod", "ps", "--format", "json"}
   126  	selectorAsList := strings.Split(selector, ",")
   127  	for _, s := range selectorAsList {
   128  		args = append(args, "--filter=label="+s)
   129  	}
   130  	cmd := exec.Command(o.podmanCmd, append(o.containerRunGlobalExtraArgs, args...)...)
   131  	klog.V(3).Infof("executing %v", cmd.Args)
   132  	out, err := cmd.Output()
   133  	if err != nil {
   134  		if exiterr, ok := err.(*exec.ExitError); ok {
   135  			err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr))
   136  		}
   137  		return nil, err
   138  	}
   139  	var list []ListPodsReport
   140  	if err = json.Unmarshal(out, &list); err != nil {
   141  		return nil, err
   142  	}
   143  	return list, nil
   144  }
   145  
   146  type podWatcher struct {
   147  	stop   chan struct{}
   148  	pods   map[string]struct{}
   149  	events chan watch.Event
   150  }
   151  
   152  func (o *PodmanCli) PodWatcher(ctx context.Context, selector string) (watch.Interface, error) {
   153  
   154  	watcher := podWatcher{
   155  		stop:   make(chan struct{}),
   156  		pods:   make(map[string]struct{}),
   157  		events: make(chan watch.Event),
   158  	}
   159  	go watcher.watch(o.podmanCmd, o.containerRunGlobalExtraArgs)
   160  	return watcher, nil
   161  }
   162  
   163  func (o podWatcher) watch(podmanCmd string, containerRunGlobalExtraArgs []string) {
   164  	args := []string{"ps", "--quiet"}
   165  	args = append(containerRunGlobalExtraArgs, args...)
   166  	ticker := time.NewTicker(3 * time.Second)
   167  	for {
   168  		select {
   169  		case <-o.stop:
   170  			return
   171  		case <-ticker.C:
   172  			cmd := exec.Command(podmanCmd, args...)
   173  			out, err := cmd.Output()
   174  			if err != nil {
   175  				klog.V(4).Infof("error getting containers from podman: %s", err)
   176  				continue
   177  			}
   178  			scanner := bufio.NewScanner(bytes.NewReader(out))
   179  			currentPods := make(map[string]struct{})
   180  			for scanner.Scan() {
   181  				podName := scanner.Text()
   182  				currentPods[podName] = struct{}{}
   183  				if _, ok := o.pods[podName]; !ok {
   184  					o.events <- watch.Event{
   185  						Type: watch.Added,
   186  					}
   187  					o.pods[podName] = struct{}{}
   188  				}
   189  			}
   190  			for p := range o.pods {
   191  				if _, ok := currentPods[p]; !ok {
   192  					o.events <- watch.Event{
   193  						Type: watch.Deleted,
   194  					}
   195  					delete(o.pods, p)
   196  				}
   197  			}
   198  		}
   199  	}
   200  }
   201  
   202  func (o podWatcher) Stop() {
   203  	o.stop <- struct{}{}
   204  }
   205  
   206  func (o podWatcher) ResultChan() <-chan watch.Event {
   207  	return o.events
   208  }
   209  

View as plain text