From c7e7123f8e5a66c7687cec1bc1e19331b6485513 Mon Sep 17 00:00:00 2001 From: Clinton Knight Date: Wed, 18 Oct 2017 13:13:08 -0400 Subject: [PATCH] Use 'oc' in tridentctl in OpenShift environments --- CHANGELOG.md | 1 + README.md | 27 +++++++++++++++++---- cli/cmd/logs.go | 12 +++++----- cli/cmd/root.go | 64 ++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 80 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7a9bfa7b..1859dffc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Added support for NetApp Volume Encryption to the ONTAP drivers (Issue [#3](https://github.com/NetApp/trident/issues/3)). - Trident installer now works with Kubernetes 1.8. +- tridentctl can detect and use 'oc' in OpenShift environments. ## v17.07.0 diff --git a/README.md b/README.md index 4aea872ba..81345868a 100644 --- a/README.md +++ b/README.md @@ -818,7 +818,7 @@ The current attributes and their possible values are below: | provisioningType | string | thin, thick | Types of provisioning supported by the storage pool. | Whether volumes will be created with thick or thin provisioning. | thick provisioning: ontap-nas, ontap-nas-economy, ontap-san, eseries-iscsi; thin provisioning: ontap-nas, ontap-nas-economy, ontap-san, solidfire-san | | backendType | string | ontap-nas, ontap-nas-economy, ontap-san, solidfire-san, eseries-iscsi | Backend to which the storage pool belongs. | Specific type of backend on which to provision volumes. | All drivers | | snapshots | bool | true, false | Whether the backend supports snapshots. | Whether volumes must have snapshot support. | ontap-nas, ontap-san, solidfire-san | -| encryption | bool | true, false | Whether the backend supports volume encryption. | Whether volumes must be encrypted. | +| encryption | bool | true, false | Whether the backend supports volume encryption. | Whether volumes must be encrypted. | ontap-nas, ontap-san, ontap-nas-economy | | IOPS | int | positive integers | IOPS range the storage pool is capable of providing. | Target IOPS for the volume to be created. | solidfire-san | @@ -849,7 +849,7 @@ a command-line utility, `tridentctl`, that provides simple access to Trident. `tridentctl` can be used to interact with Trident when deployed as a Kubernetes pod or as a standalone binary. Kubernetes users with sufficient privileges to manage the namespace that contains the Trident pod may use tridentctl. -Here are a few examples one can use `tridentctl` for to manage Trident backends: +Here are a few `tridentctl` examples for managing Trident backends: 1. Add a new storage backend or update an existing one: ```bash @@ -867,6 +867,18 @@ Here are a few examples one can use `tridentctl` for to manage Trident backends: $ tridentctl get backend -o json ``` +4. List all backends managed by Trident, if Trident is running as a standalone +binary. The address and port values are set via arguments to the Trident binary. + ```bash + $ export TRIDENT_SERVER=127.0.0.1:8000 + $ tridentctl get backend + ``` + +5. Get the logs from Trident: + ```bash + $ tridentctl logs + ``` + Similar commands can be used to list and retrieve the details for volumes and storage classes managed by Trident. You can get more information by running `tridentctl --help`. `tridentctl` currently supports adding, updating, listing, @@ -1173,9 +1185,16 @@ changes since v1.0). ### Getting Help -Trident is a supported NetApp project. See the [find the support you need](http://mysupport.netapp.com/info/web/ECMLP2619434.html) page on the Support site for options available to you. To open a support case, use the serial number of the backend storage system and select containers and Trident as the category you want help in. +Trident is a supported NetApp project. See the [find the support you need](http://mysupport.netapp.com/info/web/ECMLP2619434.html) +page on the Support site for options available to you. To open a support case, +use the serial number of the backend storage system and select containers and +Trident as the category you want help in. To create a support archive containing +all available Trident logs, use `tridentctl logs -a`. -There is also a vibrant community of container users and engineers on Pub's [#containers](https://netapppub.slack.com/messages/C1E3QH84C) Slack channel. This can be a great place to get answers and discuss with like-minded peers; highly recommended! +There is also a vibrant community of container users and engineers on the +[#containers](https://netapppub.slack.com/messages/C1E3QH84C) Slack channel at +[thePub](http://netapp.io). This can be a great place to get answers and discuss +with like-minded peers; highly recommended! ## Caveats diff --git a/cli/cmd/logs.go b/cli/cmd/logs.go index 54d5dcb23..2719019b8 100644 --- a/cli/cmd/logs.go +++ b/cli/cmd/logs.go @@ -208,16 +208,16 @@ func getTridentLogs(log string, logMap map[string][]byte) error { return fmt.Errorf("%s is not a valid Trident log", log) } - // Build command for 'kubectl logs' + // Build command to get K8S logs limit := fmt.Sprintf("--limit-bytes=%d", LOG_LIMIT_BYTES) logsCommand := []string{"logs", TridentPodName, "-n", TridentPodNamespace, "-c", container, limit} if Debug { - fmt.Printf("Invoking command: kubectl %v\n", strings.Join(logsCommand, " ")) + fmt.Printf("Invoking command: %s %v\n", KubernetesCLI, strings.Join(logsCommand, " ")) } // Get logs - logBytes, err := exec.Command("kubectl", logsCommand...).CombinedOutput() + logBytes, err := exec.Command(KubernetesCLI, logsCommand...).CombinedOutput() if err != nil { logMap["error"] = appendError(logMap["error"], logBytes) } else { @@ -239,16 +239,16 @@ func getPodLogs(log string, logMap map[string][]byte) error { return fmt.Errorf("%s is not a valid Trident log", log) } - // Build command for 'kubectl logs' + // Build command to get K8S logs limit := fmt.Sprintf("--limit-bytes=%d", LOG_LIMIT_BYTES) logsCommand := []string{"logs", pod, "-n", TridentPodNamespace, limit} if Debug { - fmt.Printf("Invoking command: kubectl %v\n", strings.Join(logsCommand, " ")) + fmt.Printf("Invoking command: %s %v\n", KubernetesCLI, strings.Join(logsCommand, " ")) } // Get logs - logBytes, err := exec.Command("kubectl", logsCommand...).CombinedOutput() + logBytes, err := exec.Command(KubernetesCLI, logsCommand...).CombinedOutput() if err != nil { logMap["error"] = appendError(logMap["error"], logBytes) } else { diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 282f550a4..3eda891b8 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -23,6 +24,9 @@ const ( MODE_TUNNEL = "tunnel" MODE_LOGS = "logs" + CLI_KUBERNETES = "kubectl" + CLI_OPENSHIFT = "oc" + POD_SERVER = "127.0.0.1:8000" EXIT_CODE_SUCCESS = 0 @@ -31,6 +35,7 @@ const ( var ( OperatingMode string + KubernetesCLI string TridentPodName string TridentPodNamespace string ExitCode int @@ -69,10 +74,11 @@ func discoverOperatingMode(cmd *cobra.Command) error { case MODE_DIRECT: fmt.Printf("Operating mode = %s, Server = %s\n", OperatingMode, Server) case MODE_TUNNEL: - fmt.Printf("Operating mode = %s, Trident pod = %s, Namespace = %s\n", - OperatingMode, TridentPodName, TridentPodNamespace) + fmt.Printf("Operating mode = %s, Trident pod = %s, Namespace = %s, CLI = %s\n", + OperatingMode, TridentPodName, TridentPodNamespace, KubernetesCLI) case MODE_LOGS: - fmt.Printf("Operating mode = %s, Namespace = %s\n", OperatingMode, TridentPodNamespace) + fmt.Printf("Operating mode = %s, Namespace = %s, CLI = %s\n", + OperatingMode, TridentPodNamespace, KubernetesCLI) } }() @@ -93,6 +99,12 @@ func discoverOperatingMode(cmd *cobra.Command) error { return nil } + // To work with pods, we need to discover which CLI to invoke + err = discoverKubernetesCLI() + if err != nil { + return err + } + // Server not specified, so try tunneling to a pod if TridentPodNamespace == "" { TridentPodNamespace, err = getCurrentNamespace() @@ -117,10 +129,29 @@ func discoverOperatingMode(cmd *cobra.Command) error { return nil } +func discoverKubernetesCLI() error { + + // Try the OpenShift CLI first + _, err := exec.Command(CLI_OPENSHIFT, "version").CombinedOutput() + if GetExitCodeFromError(err) == EXIT_CODE_SUCCESS { + KubernetesCLI = CLI_OPENSHIFT + return nil + } + + // Fall back to the K8S CLI + _, err = exec.Command(CLI_KUBERNETES, "version").CombinedOutput() + if GetExitCodeFromError(err) == EXIT_CODE_SUCCESS { + KubernetesCLI = CLI_KUBERNETES + return nil + } + + return errors.New("Could not find the Kubernetes CLI.") +} + func getCurrentNamespace() (string, error) { // Get current namespace from service account info - cmd := exec.Command("kubectl", "get", "serviceaccount", "default", "-o=json") + cmd := exec.Command(KubernetesCLI, "get", "serviceaccount", "default", "-o=json") stdout, err := cmd.StdoutPipe() if err != nil { return "", err @@ -148,7 +179,7 @@ func getCurrentNamespace() (string, error) { func getTridentPod(namespace string) (string, error) { // Get 'trident' pod info - cmd := exec.Command("kubectl", "get", "pod", "-n", namespace, "-l", "app=trident.netapp.io", "-o=json") + cmd := exec.Command(KubernetesCLI, "get", "pod", "-n", namespace, "-l", "app=trident.netapp.io", "-o=json") stdout, err := cmd.StdoutPipe() if err != nil { return "", err @@ -192,7 +223,7 @@ func GetBaseURL() (string, error) { func TunnelCommand(commandArgs []string) { - // Build tunnel command for 'kubectl exec' + // Build tunnel command to exec command in container execCommand := []string{"exec", TridentPodName, "-n", TridentPodNamespace, "-c", config.ContainerTrident, "--"} // Build CLI command @@ -209,11 +240,11 @@ func TunnelCommand(commandArgs []string) { execCommand = append(execCommand, cliCommand...) if Debug { - fmt.Printf("Invoking tunneled command: kubectl %v\n", strings.Join(execCommand, " ")) + fmt.Printf("Invoking tunneled command: %s %v\n", KubernetesCLI, strings.Join(execCommand, " ")) } // Invoke tridentctl inside the Trident pod - out, err := exec.Command("kubectl", execCommand...).CombinedOutput() + out, err := exec.Command(KubernetesCLI, execCommand...).CombinedOutput() SetExitCodeFromError(err) if err != nil { @@ -225,7 +256,7 @@ func TunnelCommand(commandArgs []string) { func TunnelCommandRaw(commandArgs []string) ([]byte, error) { - // Build tunnel command for 'kubectl exec' + // Build tunnel command to exec command in container execCommand := []string{"exec", TridentPodName, "-n", TridentPodNamespace, "-c", config.ContainerTrident, "--"} // Build CLI command @@ -236,28 +267,33 @@ func TunnelCommandRaw(commandArgs []string) ([]byte, error) { execCommand = append(execCommand, cliCommand...) if Debug { - fmt.Printf("Invoking tunneled command: kubectl %v\n", strings.Join(execCommand, " ")) + fmt.Printf("Invoking tunneled command: %s %v\n", KubernetesCLI, strings.Join(execCommand, " ")) } // Invoke tridentctl inside the Trident pod - output, err := exec.Command("kubectl", execCommand...).CombinedOutput() + output, err := exec.Command(KubernetesCLI, execCommand...).CombinedOutput() SetExitCodeFromError(err) return output, err } func SetExitCodeFromError(err error) { + ExitCode = GetExitCodeFromError(err) +} +func GetExitCodeFromError(err error) int { if err == nil { - ExitCode = EXIT_CODE_SUCCESS + return EXIT_CODE_SUCCESS } else { // Default to 1 in case we can't determine a process exit code - ExitCode = EXIT_CODE_FAILURE + code := EXIT_CODE_FAILURE if exitError, ok := err.(*exec.ExitError); ok { ws := exitError.Sys().(syscall.WaitStatus) - ExitCode = ws.ExitStatus() + code = ws.ExitStatus() } + + return code } }