This is an automated email from the ASF dual-hosted git repository.

rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 06a327a5da Fix tc-health-client env special chars (#6933)
06a327a5da is described below

commit 06a327a5da4bf655dc05cbb5fbe37842753cc152
Author: Robert O Butts <[email protected]>
AuthorDate: Tue Jun 28 15:41:37 2022 -0600

    Fix tc-health-client env special chars (#6933)
---
 CHANGELOG.md                           |  1 +
 tc-health-client/config/config.go      | 84 ++++++++++++++++++----------------
 tc-health-client/config/config_test.go | 44 ++++++++++++++++++
 3 files changed, 90 insertions(+), 39 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 862c0cb548..2e965a12c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -59,6 +59,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - [#6712](https://github.com/apache/trafficcontrol/issues/6712) - Fixed error 
when loading the Traffic Vault schema from `create_tables.sql` more than once.
 - [#6834](https://github.com/apache/trafficcontrol/issues/6834) - In API 4.0, 
fixed `GET` for `/servers` to display all profiles irrespective of the index 
position. Also, replaced query param `profileId` with `profileName`.
 - [#6299](https://github.com/apache/trafficcontrol/issues/6299) User 
representations don't match
+- [#6933](https://github.com/apache/trafficcontrol/issues/6933) Fixed 
tc-health-client to handle credentials files with special characters in 
variables
 - [#6776](https://github.com/apache/trafficcontrol/issues/6776) User 
properties only required sometimes
 
 ### Removed
diff --git a/tc-health-client/config/config.go 
b/tc-health-client/config/config.go
index 99dc1d870c..12f526115b 100644
--- a/tc-health-client/config/config.go
+++ b/tc-health-client/config/config.go
@@ -20,7 +20,6 @@ package config
  */
 
 import (
-       "bufio"
        "crypto/md5"
        "encoding/binary"
        "encoding/json"
@@ -34,6 +33,7 @@ import (
        "strings"
        "time"
 
+       "github.com/apache/trafficcontrol/cache-config/t3cutil"
        "github.com/apache/trafficcontrol/lib/go-log"
        "github.com/apache/trafficcontrol/tc-health-client/util"
        toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
@@ -126,47 +126,13 @@ func ReadCredentials(cfg *Cfg, updating bool) error {
                return nil
        }
 
-       f, err := os.Open(fn.Filename)
+       err := error(nil)
+       cfg.TOUrl, cfg.TOUser, cfg.TOPass, err = 
getCredentialsFromFile(cfg.CredentialFile.Filename)
        if err != nil {
-               return errors.New("failed to open + " + fn.Filename + " :" + 
err.Error())
+               return errors.New("reading credentials from file '" + 
fn.Filename + "' :" + err.Error())
        }
-       defer f.Close()
 
-       var to_pass_found = false
-       var to_url_found = false
-       var to_user_found = false
-
-       scanner := bufio.NewScanner(f)
-       for scanner.Scan() {
-               line := strings.TrimSpace(scanner.Text())
-               if strings.HasPrefix(line, "#") {
-                       continue
-               }
-               fields := strings.Split(line, " ")
-               for _, v := range fields {
-                       if strings.HasPrefix(v, "TO_") {
-                               sf := strings.Split(v, "=")
-                               if len(sf) == 2 {
-                                       if sf[0] == "TO_URL" {
-                                               // parse the url after trimming 
off any surrounding double quotes
-                                               cfg.TOUrl = strings.Trim(sf[1], 
"\"")
-                                               to_url_found = true
-                                       }
-                                       if sf[0] == "TO_USER" {
-                                               // set the TOUser after 
trimming off any surrounding quotes.
-                                               cfg.TOUser = 
strings.Trim(sf[1], "\"")
-                                               to_user_found = true
-                                       }
-                                       // set the TOPass after trimming off 
any surrounding quotes.
-                                       if sf[0] == "TO_PASS" {
-                                               cfg.TOPass = 
strings.Trim(sf[1], "\"")
-                                               to_pass_found = true
-                                       }
-                               }
-                       }
-               }
-       }
-       if !to_url_found && !to_user_found && !to_pass_found {
+       if cfg.TOUrl == "" || cfg.TOUser == "" || cfg.TOPass == "" {
                return errors.New("failed to retrieve one or more TrafficOps 
credentails")
        }
 
@@ -415,3 +381,43 @@ func UpdateConfig(cfg *Cfg, newCfg *Cfg) {
 func Usage() {
        getopt.PrintUsage(os.Stdout)
 }
+
+// getCredentialsFromFile gets the TO URL, user, and password from an 
environment variable file.
+// from environment variables declared in a credentials file bash script, if 
they exist.
+//
+// Returns the TO URL, user, password, and any error.
+//
+// Note this returns empty strings with no error if the file doesn't exist,
+// or if any variables aren't declared.
+//
+func getCredentialsFromFile(filePath string) (string, string, string, error) {
+
+       if inf, err := os.Stat(filePath); os.IsNotExist(err) {
+               return "", "", "", nil
+       } else if inf.IsDir() {
+               return "", "", "", errors.New("credentials path is a directory, 
must be a file")
+       }
+
+       // we execute sh and source the file to get the environment variables,
+       // because it's easier and more accurate than writing our own sh env 
var parser.
+
+       stdOut, stdErr, code := t3cutil.Do("sh", "-c", `(source "`+filePath+`" 
&& printf "${TO_URL}\n")`)
+       if code != 0 {
+               return "", "", "", fmt.Errorf("getting credentials from file 
returned error code %v stderr '%v' stdout '%v'", code, string(stdErr), 
string(stdOut))
+       }
+       toURL := strings.TrimSpace(string(stdOut))
+
+       stdOut, stdErr, code = t3cutil.Do("sh", "-c", `(source "`+filePath+`" 
&& printf "${TO_USER}\n")`)
+       if code != 0 {
+               return "", "", "", fmt.Errorf("getting credentials from file 
returned error code %v stderr '%v' stdout '%v'", code, string(stdErr), 
string(stdOut))
+       }
+       toUser := strings.TrimSpace(string(stdOut))
+
+       stdOut, stdErr, code = t3cutil.Do("sh", "-c", `(source "`+filePath+`" 
&& printf "${TO_PASS}\n")`)
+       if code != 0 {
+               return "", "", "", fmt.Errorf("getting credentials from file 
returned error code %v stderr '%v' stdout '%v'", code, string(stdErr), 
string(stdOut))
+       }
+       toPass := strings.TrimSpace(string(stdOut))
+
+       return toURL, toUser, toPass, nil
+}
diff --git a/tc-health-client/config/config_test.go 
b/tc-health-client/config/config_test.go
index 2d286494ac..db3541b0f1 100644
--- a/tc-health-client/config/config_test.go
+++ b/tc-health-client/config/config_test.go
@@ -20,6 +20,7 @@ package config
  */
 
 import (
+       "os"
        "testing"
 
        "github.com/apache/trafficcontrol/tc-health-client/util"
@@ -113,3 +114,46 @@ func TestLoadConfig(t *testing.T) {
                t.Fatalf("expected '%s', got %s\n", expect, bindir)
        }
 }
+
+func TestGetCredentialsFromFile(t *testing.T) {
+
+       fi, err := os.CreateTemp("", "creds")
+       if err != nil {
+               t.Fatalf("creating temp credentials file: %v", err)
+       }
+       defer os.Remove(fi.Name())
+
+       credsFileContents := `
+# credentials
+export TO_URL="https://trafficops.example.net";
+export TO_USER="myuser"
+export TO_PASS="mypass"
+`
+       expectedURL := `https://trafficops.example.net`
+       expectedUser := `myuser`
+       expectedPass := `mypass`
+
+       if _, err := fi.Write([]byte(credsFileContents)); err != nil {
+               t.Fatalf("writing temp credentials file: %v", err)
+       }
+
+       if err := fi.Close(); err != nil {
+               t.Fatalf("closing temp credentials file: %v", err)
+       }
+
+       credsFilePath := fi.Name()
+       toURL, toUser, toPass, err := getCredentialsFromFile(credsFilePath)
+       if err != nil {
+               t.Fatalf("getting temp credentials file: %v", err)
+       }
+       if toURL != expectedURL {
+               t.Errorf("credentials file TO URL expected '%v' actual '%v'", 
expectedURL, toURL)
+       }
+       if toUser != expectedUser {
+               t.Errorf("credentials file TO User expected '%v' actual '%v'", 
expectedUser, toUser)
+       }
+       if toPass != expectedPass {
+               t.Errorf("credentials file TO Pass expected '%v' actual '%v'", 
expectedPass, toPass)
+       }
+
+}

Reply via email to