You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2021/08/12 08:29:31 UTC

[skywalking-infra-e2e] branch enhancement/env-var created (now 33088ee)

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

kezhenxu94 pushed a change to branch enhancement/env-var
in repository https://gitbox.apache.org/repos/asf/skywalking-infra-e2e.git.


      at 33088ee  Propagate environment variables from user command to parent process

This branch includes the following new commits:

     new 33088ee  Propagate environment variables from user command to parent process

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[skywalking-infra-e2e] 01/01: Propagate environment variables from user command to parent process

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch enhancement/env-var
in repository https://gitbox.apache.org/repos/asf/skywalking-infra-e2e.git

commit 33088ee7c4f8c15ff0c3a735eff87b204a32eefb
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Thu Aug 12 16:28:59 2021 +0800

    Propagate environment variables from user command to parent process
    
    And some other enhancements:
    
    - Replace linter `golint` to `revive` as the former is deprecated, and fix code styles found by `revive`.
    - Polish the logs to make it not too lengthy.
    - Add log level configuration.
    - Bump up Go version to 1.16.
    - Propagate environment variables from user command (sub-process) to parent process to make it available to other sub-processes.
---
 .golangci.yml                       |  5 +--
 Makefile                            |  2 +-
 commands/root.go                    | 30 ++++++++++++++-
 commands/verify/verify.go           | 12 +++---
 go.mod                              |  2 +-
 internal/components/cleanup/kind.go |  6 +--
 internal/components/setup/common.go |  9 +++--
 internal/components/trigger/http.go |  4 +-
 internal/logger/log.go              |  2 +-
 internal/util/config.go             |  5 ++-
 internal/util/hook.sh               | 21 +++++++++++
 internal/util/{config.go => io.go}  | 34 ++++++++---------
 internal/util/utils.go              | 74 ++++++++++++++++++++++++++++++++++---
 13 files changed, 156 insertions(+), 50 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index 617deaf..f3d33b5 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -24,7 +24,7 @@ run:
 linters-settings:
   govet:
     check-shadowing: true
-  golint:
+  revive:
     min-confidence: 0
   gocyclo:
     min-complexity: 15
@@ -108,12 +108,11 @@ linters:
     - gocyclo
     - gofmt
     - goimports
-    - golint
+    - revive
     - gosec
     - gosimple
     - govet
     - ineffassign
-    - interfacer
     - lll
     - misspell
     - nakedret
diff --git a/Makefile b/Makefile
index 1eee4be..e0f675b 100644
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,7 @@ all: clean lint test build
 
 .PHONY: lint
 lint:
-	$(GO_LINT) version || curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GO_PATH)/bin
+	$(GO_LINT) version || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GO_PATH)/bin
 	$(GO_LINT) run -v --timeout 5m ./...
 
 .PHONY: fix-lint
diff --git a/commands/root.go b/commands/root.go
index 8b5fe6c..ff625db 100644
--- a/commands/root.go
+++ b/commands/root.go
@@ -18,9 +18,12 @@
 package commands
 
 import (
+	"os"
+
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
-	"github.com/apache/skywalking-infra-e2e/internal/util"
+	"github.com/apache/skywalking-infra-e2e/internal/logger"
 
 	"github.com/apache/skywalking-infra-e2e/commands/cleanup"
 	"github.com/apache/skywalking-infra-e2e/commands/run"
@@ -29,6 +32,11 @@ import (
 	"github.com/apache/skywalking-infra-e2e/commands/verify"
 	"github.com/apache/skywalking-infra-e2e/internal/config"
 	"github.com/apache/skywalking-infra-e2e/internal/constant"
+	"github.com/apache/skywalking-infra-e2e/internal/util"
+)
+
+var (
+	verbosity string
 )
 
 // Root represents the base command when called without any subcommands
@@ -38,8 +46,24 @@ var Root = &cobra.Command{
 	Version:       version,
 	SilenceErrors: true,
 	SilenceUsage:  true,
-	PersistentPreRun: func(cmd *cobra.Command, args []string) {
+	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
 		config.ReadGlobalConfigFile()
+
+		level, err := logrus.ParseLevel(verbosity)
+		if err != nil {
+			return err
+		}
+		logger.Log.SetLevel(level)
+
+		util.WorkDir = util.ExpandFilePath(util.WorkDir)
+		if _, err := os.Stat(util.WorkDir); os.IsNotExist(err) {
+			if err := os.MkdirAll(util.WorkDir, os.ModePerm); err != nil {
+				logger.Log.Warnf("failed to create working directory %v", util.WorkDir)
+				return err
+			}
+		}
+
+		return nil
 	},
 }
 
@@ -52,6 +76,8 @@ func Execute() error {
 	Root.AddCommand(verify.Verify)
 	Root.AddCommand(cleanup.Cleanup)
 
+	Root.PersistentFlags().StringVarP(&verbosity, "verbosity", "v", logrus.InfoLevel.String(), "log level (debug, info, warn, error, fatal, panic")
+	Root.PersistentFlags().StringVarP(&util.WorkDir, "work-dir", "w", "~/.skywalking-infra-e2e", "the working directory for skywalking-infra-e2e")
 	Root.PersistentFlags().StringVarP(&util.CfgFile, "config", "c", constant.E2EDefaultFile, "the config file")
 
 	return Root.Execute()
diff --git a/commands/verify/verify.go b/commands/verify/verify.go
index e979b41..776510b 100644
--- a/commands/verify/verify.go
+++ b/commands/verify/verify.go
@@ -59,7 +59,7 @@ func verifySingleCase(expectedFile, actualFile, query string) error {
 		return fmt.Errorf("failed to read the expected data file: %v", err)
 	}
 
-	var actualData, sourceName string
+	var actualData, sourceName, stderr string
 	if actualFile != "" {
 		sourceName = actualFile
 		actualData, err = util.ReadFileContent(actualFile)
@@ -68,19 +68,19 @@ func verifySingleCase(expectedFile, actualFile, query string) error {
 		}
 	} else if query != "" {
 		sourceName = query
-		actualData, err = util.ExecuteCommand(query)
+		actualData, stderr, err = util.ExecuteCommand(query)
 		if err != nil {
-			return fmt.Errorf("failed to execute the query: %s, output: %s, error: %v", query, actualData, err)
+			return fmt.Errorf("failed to execute the query: %s, output: %s, error: %v", query, actualData, stderr)
 		}
 	}
 
 	if err = verifier.Verify(actualData, expectedData); err != nil {
 		if me, ok := err.(*verifier.MismatchError); ok {
-			return fmt.Errorf("failed to verify the output: %s, error: %v", sourceName, me.Error())
+			return fmt.Errorf("failed to verify the output: %s, error:\n%v", sourceName, me.Error())
 		}
-		return fmt.Errorf("failed to verify the output: %s, error: %v", sourceName, err)
+		return fmt.Errorf("failed to verify the output: %s, error:\n%v", sourceName, err)
 	}
-	logger.Log.Infof("verified the output: %s\n", sourceName)
+	logger.Log.Infof("verified the output: %s", sourceName)
 	return nil
 }
 
diff --git a/go.mod b/go.mod
index ce51fdd..004f535 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/apache/skywalking-infra-e2e
 
-go 1.13
+go 1.16
 
 require (
 	github.com/docker/docker v20.10.7+incompatible
diff --git a/internal/components/cleanup/kind.go b/internal/components/cleanup/kind.go
index 1e19072..3e5f8f1 100644
--- a/internal/components/cleanup/kind.go
+++ b/internal/components/cleanup/kind.go
@@ -86,9 +86,5 @@ func cleanKindCluster(kindConfigFilePath string) error {
 	args := []string{"delete", "cluster", "--name", clusterName}
 
 	logger.Log.Debugf("cluster delete commands: %s %s", constant.KindCommand, strings.Join(args, " "))
-	if err := kind.Run(kindcmd.NewLogger(), kindcmd.StandardIOStreams(), args); err != nil {
-		return err
-	}
-
-	return nil
+	return kind.Run(kindcmd.NewLogger(), kindcmd.StandardIOStreams(), args)
 }
diff --git a/internal/components/setup/common.go b/internal/components/setup/common.go
index 1e93b9d..e220fa8 100644
--- a/internal/components/setup/common.go
+++ b/internal/components/setup/common.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"strings"
 	"time"
 
 	"k8s.io/client-go/dynamic"
@@ -170,13 +171,13 @@ func executeCommandsAndWait(commands string, waits []config.Wait, waitSet *util.
 	defer waitSet.WaitGroup.Done()
 
 	// executes commands
-	logger.Log.Infof("executing commands [%s]", commands)
-	result, err := util.ExecuteCommand(commands)
+	logger.Log.Infof("executing commands [%s]", strings.ReplaceAll(commands, "\n", "\\n"))
+	result, stderr, err := util.ExecuteCommand(commands)
 	if err != nil {
-		err = fmt.Errorf("commands: [%s] runs error: %s", commands, err)
+		err = fmt.Errorf("commands: [%s] runs error: %s", strings.ReplaceAll(commands, "\n", "\\n"), stderr)
 		waitSet.ErrChan <- err
 	}
-	logger.Log.Infof("executed commands [%s], result: %s", commands, result)
+	logger.Log.Infof("executed commands [%s], result: %s", strings.ReplaceAll(commands, "\n", "\\n"), result)
 
 	// waits for conditions meet
 	for idx := range waits {
diff --git a/internal/components/trigger/http.go b/internal/components/trigger/http.go
index ce4cbe2..2af36a3 100644
--- a/internal/components/trigger/http.go
+++ b/internal/components/trigger/http.go
@@ -113,9 +113,9 @@ func (h *httpAction) executeOnce(client *http.Client, req *http.Request) error {
 		logger.Log.Errorf("do request error %v", err)
 		return err
 	}
-	response.Body.Close()
+	_ = response.Body.Close()
 
-	logger.Log.Infof("do request %v response http code %v", h.url, response.StatusCode)
+	logger.Log.Debugf("do request %v response http code %v", h.url, response.StatusCode)
 	if response.StatusCode == http.StatusOK {
 		logger.Log.Debugf("do http action %+v success.", *h)
 		return nil
diff --git a/internal/logger/log.go b/internal/logger/log.go
index 7fc102e..1c641e8 100644
--- a/internal/logger/log.go
+++ b/internal/logger/log.go
@@ -29,7 +29,7 @@ func init() {
 	if Log == nil {
 		Log = logrus.New()
 	}
-	Log.Level = logrus.DebugLevel
+	Log.Level = logrus.InfoLevel
 	Log.SetOutput(os.Stdout)
 	Log.SetFormatter(&logrus.TextFormatter{
 		DisableTimestamp:       true,
diff --git a/internal/util/config.go b/internal/util/config.go
index 15e38e1..c34eecb 100644
--- a/internal/util/config.go
+++ b/internal/util/config.go
@@ -25,7 +25,10 @@ import (
 	"github.com/apache/skywalking-infra-e2e/internal/logger"
 )
 
-var CfgFile string
+var (
+	CfgFile string
+	WorkDir string
+)
 
 // ResolveAbs resolves the relative path (relative to CfgFile) to an absolute file path.
 func ResolveAbs(p string) string {
diff --git a/internal/util/hook.sh b/internal/util/hook.sh
new file mode 100644
index 0000000..0a09eda
--- /dev/null
+++ b/internal/util/hook.sh
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+function finish {
+  printenv > {{ .EnvFile }}
+}
+trap finish EXIT
diff --git a/internal/util/config.go b/internal/util/io.go
similarity index 62%
copy from internal/util/config.go
copy to internal/util/io.go
index 15e38e1..733f552 100644
--- a/internal/util/config.go
+++ b/internal/util/io.go
@@ -19,29 +19,27 @@
 package util
 
 import (
-	"path"
-	"path/filepath"
+	"os/user"
+	"strings"
 
 	"github.com/apache/skywalking-infra-e2e/internal/logger"
 )
 
-var CfgFile string
-
-// ResolveAbs resolves the relative path (relative to CfgFile) to an absolute file path.
-func ResolveAbs(p string) string {
-	if p == "" {
-		return p
-	}
-
-	if path.IsAbs(p) {
-		return p
+// UserHomeDir returns the current user's home directory absolute path,
+// which is usually represented as `~` in most shells
+func UserHomeDir() string {
+	if currentUser, err := user.Current(); err != nil {
+		logger.Log.Warnln("Cannot obtain user home directory")
+	} else {
+		return currentUser.HomeDir
 	}
+	return ""
+}
 
-	abs, err := filepath.Abs(CfgFile)
-	if err != nil {
-		logger.Log.Warnf("failed to resolve the absolute file path of %v\n", CfgFile)
-		return p
+// ExpandFilePath expands the leading `~` to absolute path
+func ExpandFilePath(path string) string {
+	if strings.HasPrefix(path, "~") {
+		return strings.Replace(path, "~", UserHomeDir(), 1)
 	}
-
-	return filepath.Join(filepath.Dir(abs), p)
+	return path
 }
diff --git a/internal/util/utils.go b/internal/util/utils.go
index 1e84ea1..5dc7a55 100644
--- a/internal/util/utils.go
+++ b/internal/util/utils.go
@@ -20,10 +20,16 @@ package util
 
 import (
 	"bytes"
+	_ "embed"
 	"errors"
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path/filepath"
+	"strings"
+	"text/template"
+
+	"github.com/apache/skywalking-infra-e2e/internal/logger"
 )
 
 // PathExist checks if a file/directory is exist.
@@ -48,16 +54,72 @@ func ReadFileContent(filename string) (string, error) {
 }
 
 // ExecuteCommand executes the given command and returns the result.
-func ExecuteCommand(cmd string) (string, error) {
+func ExecuteCommand(cmd string) (stdout, stderr string, err error) {
+	hookScript, err := hookScript()
+	if err != nil {
+		return "", "", err
+	}
+
+	// Propagate the env vars from sub-process back to parent process
+	defer exportEnvVars()
+
+	cmd = hookScript + "\n" + cmd
+
 	command := exec.Command("bash", "-ec", cmd)
-	outinfo := bytes.Buffer{}
-	command.Stdout = &outinfo
+	sout, serr := bytes.Buffer{}, bytes.Buffer{}
+	command.Stdout, command.Stderr = &sout, &serr
 
 	if err := command.Start(); err != nil {
-		return outinfo.String(), err
+		return sout.String(), serr.String(), err
 	}
 	if err := command.Wait(); err != nil {
-		return outinfo.String(), err
+		return sout.String(), serr.String(), err
+	}
+	return sout.String(), serr.String(), nil
+}
+
+//go:embed hook.sh
+var hookScriptTemplate string
+
+type HookScriptTemplate struct {
+	EnvFile string
+}
+
+func hookScript() (string, error) {
+	hookScript := bytes.Buffer{}
+
+	parse, err := template.New("hookScriptTemplate").Parse(hookScriptTemplate)
+	if err != nil {
+		return "", err
+	}
+
+	envFile := filepath.Join(WorkDir, ".env")
+	scriptData := HookScriptTemplate{EnvFile: envFile}
+	if err := parse.Execute(&hookScript, scriptData); err != nil {
+		return "", err
+	}
+	return hookScript.String(), nil
+}
+
+func exportEnvVars() {
+	envFile := filepath.Join(WorkDir, ".env")
+	b, err := ioutil.ReadFile(envFile)
+	if err != nil {
+		logger.Log.Warnf("failed to export environment variables, %v", err)
+		return
+	}
+	s := string(b)
+
+	lines := strings.Split(s, "\n")
+	for _, line := range lines {
+		kv := strings.SplitN(line, "=", 2)
+		if len(kv) != 2 {
+			continue
+		}
+		key, val := kv[0], kv[1]
+		// should only export env vars that are not already exist in parent process (Go process)
+		if err := os.Setenv(key, val); err != nil {
+			logger.Log.Warnf("failed to export environment variable %v=%v, %v", key, val, err)
+		}
 	}
-	return outinfo.String(), nil
 }