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 }