github.com/jenkins-x/draft-repo@v0.9.0/pkg/draft/local/local.go (about)

     1  package local
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/BurntSushi/toml"
     8  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     9  	"k8s.io/apimachinery/pkg/labels"
    10  	"k8s.io/client-go/kubernetes"
    11  	"k8s.io/client-go/pkg/api/v1"
    12  	restclient "k8s.io/client-go/rest"
    13  	"k8s.io/helm/pkg/kube"
    14  
    15  	"github.com/Azure/draft/pkg/draft/manifest"
    16  )
    17  
    18  // DraftLabelKey is the label selector key on a pod that allows
    19  //  us to identify which draft app a pod is associated with
    20  const DraftLabelKey = "draft"
    21  
    22  type App struct {
    23  	Name      string
    24  	Namespace string
    25  	Container string
    26  }
    27  
    28  type Connection struct {
    29  	Tunnel    *kube.Tunnel
    30  	PodName   string
    31  	Clientset kubernetes.Interface
    32  }
    33  
    34  // DeployedApplication returns deployment information about the deployed instance
    35  //  of the source code given a path to your draft.toml file and the name of the
    36  //  draft environment
    37  func DeployedApplication(draftTomlPath, draftEnvironment string) (*App, error) {
    38  	var draftConfig manifest.Manifest
    39  	if _, err := toml.DecodeFile(draftTomlPath, &draftConfig); err != nil {
    40  		return nil, err
    41  	}
    42  	appConfig := draftConfig.Environments[draftEnvironment]
    43  
    44  	return &App{Name: appConfig.Name, Namespace: appConfig.Namespace}, nil
    45  }
    46  
    47  // Connect creates a local tunnel to a Kubernetes pod running the application and returns the connection information
    48  func (a *App) Connect(clientset kubernetes.Interface, clientConfig *restclient.Config) (*Connection, error) {
    49  	tunnel, podName, err := a.NewTunnel(clientset, clientConfig)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return &Connection{
    55  		Tunnel:    tunnel,
    56  		PodName:   podName,
    57  		Clientset: clientset,
    58  	}, nil
    59  }
    60  
    61  // NewTunnel creates and returns a tunnel to a Kubernetes pod running the application
    62  func (a *App) NewTunnel(clientset kubernetes.Interface, config *restclient.Config) (*kube.Tunnel, string, error) {
    63  	podName, containers, err := getAppPodNameAndContainers(a.Namespace, a.Name, clientset)
    64  	if err != nil {
    65  		return nil, "", err
    66  	}
    67  
    68  	port, err := getContainerPort(containers, a.Container)
    69  	if err != nil {
    70  		return nil, "", err
    71  	}
    72  
    73  	t := kube.NewTunnel(clientset.CoreV1().RESTClient(), config, a.Namespace, podName, port)
    74  	if err != nil {
    75  		return nil, "", err
    76  	}
    77  
    78  	return t, podName, t.ForwardPort()
    79  }
    80  
    81  func getContainerPort(containers []v1.Container, targetContainer string) (int, error) {
    82  	var port int
    83  	containerFound := false
    84  	if targetContainer != "" {
    85  		for _, container := range containers {
    86  			if container.Name == targetContainer && !containerFound {
    87  				containerFound = true
    88  				port = int(container.Ports[0].ContainerPort)
    89  			}
    90  		}
    91  
    92  		if containerFound == false {
    93  			return 0, fmt.Errorf("container '%s' not found", targetContainer)
    94  		}
    95  	} else {
    96  		// if not container is specified, default behavior is to
    97  		//  grab first ContainerPort of first container
    98  		port = int(containers[0].Ports[0].ContainerPort)
    99  	}
   100  
   101  	return port, nil
   102  
   103  }
   104  
   105  // RequestLogStream returns a stream of the application pod's logs
   106  func (c *Connection) RequestLogStream(app *App, logLines int64) (io.ReadCloser, error) {
   107  
   108  	req := c.Clientset.CoreV1().Pods(app.Namespace).GetLogs(c.PodName,
   109  		&v1.PodLogOptions{
   110  			Follow:    true,
   111  			TailLines: &logLines,
   112  			Container: app.Container,
   113  		})
   114  
   115  	return req.Stream()
   116  }
   117  
   118  func getAppPodNameAndContainers(namespace, labelVal string, clientset kubernetes.Interface) (string, []v1.Container, error) {
   119  	selector := labels.Set{DraftLabelKey: labelVal}.AsSelector()
   120  	pod, err := getFirstRunningPod(clientset, selector, namespace)
   121  	if err != nil {
   122  		return "", nil, err
   123  	}
   124  	return pod.ObjectMeta.GetName(), pod.Spec.Containers, nil
   125  }
   126  
   127  func getFirstRunningPod(clientset kubernetes.Interface, selector labels.Selector, namespace string) (*v1.Pod, error) {
   128  	options := metav1.ListOptions{LabelSelector: selector.String()}
   129  	pods, err := clientset.CoreV1().Pods(namespace).List(options)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	if len(pods.Items) < 1 {
   134  		return nil, fmt.Errorf("could not find ready pod")
   135  	}
   136  	for _, p := range pods.Items {
   137  		if v1.IsPodReady(&p) {
   138  			return &p, nil
   139  		}
   140  	}
   141  
   142  	return nil, fmt.Errorf("could not find a ready pod")
   143  }