2
0
Эх сурвалжийг харах

add logs option to cli, pipe logs when no tty for porter run

Alexander Belanger 4 жил өмнө
parent
commit
5edd48003d
2 өөрчлөгдсөн 149 нэмэгдсэн , 1 устгасан
  1. 106 0
      cli/cmd/logs.go
  2. 43 1
      cli/cmd/run.go

+ 106 - 0
cli/cmd/logs.go

@@ -0,0 +1,106 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/porter-dev/porter/cli/cmd/api"
+	"github.com/porter-dev/porter/cli/cmd/utils"
+	"github.com/spf13/cobra"
+)
+
+// logsCmd represents the "porter logs" base command when called
+// without any subcommands
+var logsCmd = &cobra.Command{
+	Use:   "logs [release]",
+	Short: "Logs the output from a given application.",
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, logs)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
+var follow bool
+
+func init() {
+	rootCmd.AddCommand(logsCmd)
+
+	logsCmd.PersistentFlags().StringVar(
+		&namespace,
+		"namespace",
+		"default",
+		"namespace of release to connect to",
+	)
+
+	logsCmd.PersistentFlags().BoolVarP(
+		&follow,
+		"follow",
+		"f",
+		false,
+		"specify if the logs should be streamed",
+	)
+}
+
+func logs(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
+	podsSimple, err := getPods(client, namespace, args[0])
+
+	if err != nil {
+		return fmt.Errorf("Could not retrieve list of pods: %s", err.Error())
+	}
+
+	// if length of pods is 0, throw error
+	var selectedPod podSimple
+
+	if len(podsSimple) == 0 {
+		return fmt.Errorf("At least one pod must exist in this deployment.")
+	} else if len(podsSimple) == 1 {
+		selectedPod = podsSimple[0]
+	} else {
+		podNames := make([]string, 0)
+
+		for _, podSimple := range podsSimple {
+			podNames = append(podNames, podSimple.Name)
+		}
+
+		selectedPodName, err := utils.PromptSelect("Select the pod:", podNames)
+
+		if err != nil {
+			return err
+		}
+
+		// find selected pod
+		for _, podSimple := range podsSimple {
+			if selectedPodName == podSimple.Name {
+				selectedPod = podSimple
+			}
+		}
+	}
+
+	var selectedContainerName string
+
+	// if the selected pod has multiple container, spawn selector
+	if len(selectedPod.ContainerNames) == 0 {
+		return fmt.Errorf("At least one pod must exist in this deployment.")
+	} else if len(selectedPod.ContainerNames) == 1 {
+		selectedContainerName = selectedPod.ContainerNames[0]
+	} else {
+		selectedContainer, err := utils.PromptSelect("Select the container:", selectedPod.ContainerNames)
+
+		if err != nil {
+			return err
+		}
+
+		selectedContainerName = selectedContainer
+	}
+
+	restConf, err := getRESTConfig(client)
+
+	if err != nil {
+		return fmt.Errorf("Could not retrieve kube credentials: %s", err.Error())
+	}
+
+	return pipePodLogsToStdout(restConf, namespace, selectedPod.Name, selectedContainerName, follow)
+}

+ 43 - 1
cli/cmd/run.go

@@ -3,6 +3,7 @@ package cmd
 import (
 	"context"
 	"fmt"
+	"io"
 	"os"
 	"strings"
 	"time"
@@ -299,7 +300,7 @@ func executeRunEphemeral(config *rest.Config, namespace, name, container string,
 	}
 
 	for i := 0; i < 5; i++ {
-		fmt.Printf("attempting connection %d/5\n", i)
+		fmt.Printf("attempting connection %d/5\n", i+1)
 
 		err = t.Safe(fn)
 
@@ -308,6 +309,13 @@ func executeRunEphemeral(config *rest.Config, namespace, name, container string,
 		}
 
 		time.Sleep(2 * time.Second)
+
+		// ugly way to catch non-TTY errors, such as when running command "echo \"hello\""
+		if i == 4 && err != nil && strings.Contains(err.Error(), "not found in pod") {
+			fmt.Printf("Could not open a shell to this container. Container logs:\n")
+
+			err = pipePodLogsToStdout(config, namespace, podName, container, false)
+		}
 	}
 
 	// delete the ephemeral pod
@@ -316,6 +324,40 @@ func executeRunEphemeral(config *rest.Config, namespace, name, container string,
 	return err
 }
 
+func pipePodLogsToStdout(config *rest.Config, namespace, name, container string, follow bool) error {
+	podLogOpts := v1.PodLogOptions{
+		Container: container,
+		Follow:    follow,
+	}
+
+	// creates the clientset
+	clientset, err := kubernetes.NewForConfig(config)
+
+	if err != nil {
+		return err
+	}
+
+	req := clientset.CoreV1().Pods(namespace).GetLogs(name, &podLogOpts)
+
+	podLogs, err := req.Stream(
+		context.Background(),
+	)
+
+	if err != nil {
+		return err
+	}
+
+	defer podLogs.Close()
+
+	_, err = io.Copy(os.Stdout, podLogs)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func getExistingPod(config *rest.Config, name, namespace string) (*v1.Pod, error) {
 	clientset, err := kubernetes.NewForConfig(config)