Skip to content

Commit 5adf908

Browse files
authored
Support save pod/container std log on the Environment (#62)
1 parent ebdfc93 commit 5adf908

14 files changed

Lines changed: 728 additions & 184 deletions

File tree

action.yaml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ inputs:
2121
e2e-file:
2222
description: File path of e2e file
2323
required: true
24+
log-dir:
25+
description: The container logs directory
26+
required: false
2427

2528
runs:
2629
using: "composite"
@@ -30,5 +33,46 @@ runs:
3033
make -C $GITHUB_ACTION_PATH docker
3134
docker run -v $(pwd):/tmp -w /tmp --entrypoint=sh docker.io/apache/e2e:latest -c "cp /usr/local/bin/e2e /tmp/e2e"
3235
install ./e2e /usr/local/bin
36+
- name: E2E Dir Generator
37+
id: 'e2e-dir-generator'
38+
shell: bash
39+
run: |
40+
WORK_DIR="${{ runner.temp }}/skywalking-infra-e2e"
41+
echo "::set-output name=work::$WORK_DIR"
42+
43+
LOG_DIR=""
44+
LOG_JOB_DIR=""
45+
if [[ "${{ inputs.log-dir }}" == "" ]]
46+
then
47+
matrix='${{ toJSON(matrix) }}'
48+
if [[ "$matrix" == "null" ]]
49+
then
50+
LOG_DIR="$WORK_DIR/logs"
51+
LOG_JOB_DIR="$LOG_DIR/${{ github.job }}"
52+
else
53+
combine_matrix=$(echo $matrix|jq -r 'to_entries|map(.value)|tostring')
54+
# remove json syntax
55+
combine_matrix=`echo $combine_matrix|sed -e 's/\[\|\]\|\"//g'`
56+
combine_matrix=`echo $combine_matrix|sed -e 's/[\{|\}]//g'`
57+
# replace to path
58+
combine_matrix=`echo $combine_matrix|sed -e 's/[^A-Za-z0-9_-]/_/g'`
59+
LOG_DIR="$WORK_DIR/logs"
60+
LOG_JOB_DIR="$LOG_DIR/${{ github.job }}_$combine_matrix"
61+
fi
62+
elif [[ "${{ inputs.log-dir }}" == /* ]]
63+
then
64+
LOG_DIR="${{ inputs.log-dir }}"
65+
LOG_JOB_DIR="${{ inputs.log-dir }}"
66+
else
67+
LOG_DIR="$WORK_DIR/${{ inputs.log-dir }}"
68+
LOG_JOB_DIR="$WORK_DIR/${{ inputs.log-dir }}"
69+
fi
70+
echo "::set-output name=log::$LOG_DIR"
71+
echo "::set-output name=log-case::$LOG_JOB_DIR"
72+
echo "SW_INFRA_E2E_LOG_DIR=$LOG_DIR" >> $GITHUB_ENV
3373
- shell: bash
34-
run: e2e run -c "${{ inputs.e2e-file }}"
74+
run: |
75+
e2e run \
76+
-c "${{ inputs.e2e-file }}" \
77+
-w "${{ steps.e2e-dir-generator.outputs.work }}" \
78+
-l "${{ steps.e2e-dir-generator.outputs.log-case }}"

commands/root.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,32 @@ var Root = &cobra.Command{
5555
}
5656
logger.Log.SetLevel(level)
5757

58-
util.WorkDir = util.ExpandFilePath(util.WorkDir)
59-
if _, err := os.Stat(util.WorkDir); os.IsNotExist(err) {
60-
if err := os.MkdirAll(util.WorkDir, os.ModePerm); err != nil {
61-
logger.Log.Warnf("failed to create working directory %v", util.WorkDir)
62-
return err
63-
}
58+
util.WorkDir, err = ExpandPathAndCreate(util.WorkDir)
59+
if err != nil {
60+
logger.Log.Warnf("failed to create working directory %v", util.WorkDir)
61+
return err
62+
}
63+
64+
util.LogDir, err = ExpandPathAndCreate(util.LogDir)
65+
if err != nil {
66+
logger.Log.Warnf("failed to create logging directory %v", util.LogDir)
67+
return err
6468
}
6569

6670
return nil
6771
},
6872
}
6973

74+
func ExpandPathAndCreate(path string) (string, error) {
75+
path = util.ExpandFilePath(path)
76+
if _, err := os.Stat(path); os.IsNotExist(err) {
77+
if err := os.MkdirAll(path, os.ModePerm); err != nil {
78+
return path, err
79+
}
80+
}
81+
return path, nil
82+
}
83+
7084
// Execute adds all child commands to the root command and sets flags appropriately.
7185
// This is called by main.main(). It only needs to happen once to the rootCmd.
7286
func Execute() error {
@@ -78,6 +92,7 @@ func Execute() error {
7892

7993
Root.PersistentFlags().StringVarP(&verbosity, "verbosity", "v", logrus.InfoLevel.String(), "log level (debug, info, warn, error, fatal, panic")
8094
Root.PersistentFlags().StringVarP(&util.WorkDir, "work-dir", "w", "~/.skywalking-infra-e2e", "the working directory for skywalking-infra-e2e")
95+
Root.PersistentFlags().StringVarP(&util.LogDir, "log-dir", "l", "~/.skywalking-infra-e2e/logs", "the container logs directory for environment")
8196
Root.PersistentFlags().StringVarP(&util.CfgFile, "config", "c", constant.E2EDefaultFile, "the config file")
8297

8398
return Root.Execute()

commands/setup/setup.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var Setup = &cobra.Command{
3838
return err
3939
}
4040

41+
defer setup.CloseLogFollower()
4142
if err := DoSetupAccordingE2E(); err != nil {
4243
return fmt.Errorf("[Setup] %s", err)
4344
}
@@ -61,6 +62,7 @@ func DoSetupAccordingE2E() error {
6162

6263
e2eConfig := config.GlobalConfig.E2EConfig
6364

65+
setup.InitLogFollower()
6466
if e2eConfig.Setup.Env == constant.Kind {
6567
err := setup.KindSetup(&e2eConfig)
6668
if err != nil {
@@ -80,6 +82,8 @@ func DoSetupAccordingE2E() error {
8082
}
8183

8284
func DoStopSetup() {
85+
// close log follower
86+
setup.CloseLogFollower()
8387
// notify clean up
8488
setup.KindCleanNotify()
8589
}

docs/en/setup/Configuration-File.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ If you want to access the resource from host, should follow these steps:
8585
url: http://${pod_foo_host}:${pod_foo_8080}/
8686
```
8787
88+
#### Log
89+
90+
The console output of each pod could be found in `${workDir}/logs/${namespace}/${podName}.log`.
91+
8892
### Compose
8993

9094
```yaml
@@ -123,6 +127,10 @@ If you want to get the service host and port mapping, should follow these steps:
123127
url: http://${oap_host}:${oap_8080}/
124128
```
125129

130+
#### Log
131+
132+
The console output of each service could be found in `${workDir}/logs/{serviceName}/std.log`.
133+
126134
## Trigger
127135

128136
After the `Setup` step is finished, use the `Trigger` step to generate traffic.

docs/en/setup/Run-E2E-Tests.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,23 @@ e2e cleanup
3131

3232
To use skywalking-infra-e2e in GitHub Actions, add a step in your GitHub workflow.
3333

34+
The working directory could be uploaded to GitHub Action Artifact after the task is completed, which contains environment variables and container logs in the environment.
35+
3436
```yaml
3537
- name: Run E2E Test
3638
uses: apache/skywalking-infra-e2e@main # always prefer to use a revision instead of `main`.
3739
with:
38-
e2e-file: e2e.yaml # need to run E2E file path
40+
e2e-file: e2e.yaml # (required)need to run E2E file path
41+
log-dir: /path/to/log/dir # (Optional)Use `<work_dir>/logs/<job_name>_<matrix_value>`(if have GHA matrix) or `<work_dir>/logs/<job_name>` in GHA, and output logs into `<work_dir>/logs` out of GHA env, such as running locally.
42+
```
43+
44+
If you want to upload the log directory to the GitHub Action Artifact when this E2E test failure, you could define the below content in your GitHub Action Job.
45+
46+
```yaml
47+
- name: Upload E2E Log
48+
uses: actions/upload-artifact@v2
49+
if: ${{ failure() }} # Only upload the artifact when E2E testing failure
50+
with:
51+
name: e2e-log
52+
path: "${{ env.SW_INFRA_E2E_LOG_DIR }}" # The SkyWalking Infra E2E action sets SW_INFRA_E2E_LOG_DIR automatically.
3953
```

internal/components/setup/common.go

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,21 @@
1919
package setup
2020

2121
import (
22+
"context"
2223
"fmt"
23-
"io/ioutil"
2424
"os"
2525
"strings"
2626
"time"
2727

28-
"k8s.io/client-go/dynamic"
29-
"k8s.io/client-go/kubernetes"
30-
3128
"github.com/apache/skywalking-infra-e2e/internal/config"
3229
"github.com/apache/skywalking-infra-e2e/internal/logger"
3330
"github.com/apache/skywalking-infra-e2e/internal/util"
3431
)
3532

33+
var (
34+
logFollower *util.ResourceLogFollower
35+
)
36+
3637
func RunStepsAndWait(steps []config.Step, waitTimeout time.Duration, k8sCluster *util.K8sClusterInfo) error {
3738
logger.Log.Debugf("wait timeout is %v", waitTimeout.String())
3839

@@ -50,7 +51,7 @@ func RunStepsAndWait(steps []config.Step, waitTimeout time.Duration, k8sCluster
5051
Path: step.Path,
5152
Waits: step.Waits,
5253
}
53-
err := createManifestAndWait(k8sCluster.Client, k8sCluster.Interface, manifest, waitTimeout)
54+
err := createManifestAndWait(k8sCluster, manifest, waitTimeout)
5455
if err != nil {
5556
return err
5657
}
@@ -60,7 +61,7 @@ func RunStepsAndWait(steps []config.Step, waitTimeout time.Duration, k8sCluster
6061
Waits: step.Waits,
6162
}
6263

63-
err := RunCommandsAndWait(command, waitTimeout)
64+
err := RunCommandsAndWait(command, waitTimeout, k8sCluster)
6465
if err != nil {
6566
return err
6667
}
@@ -79,16 +80,11 @@ func RunStepsAndWait(steps []config.Step, waitTimeout time.Duration, k8sCluster
7980
}
8081

8182
// createManifestAndWait creates manifests in k8s cluster and concurrent waits according to the manifests' wait conditions.
82-
func createManifestAndWait(c *kubernetes.Clientset, dc dynamic.Interface, manifest config.Manifest, timeout time.Duration) error {
83+
func createManifestAndWait(c *util.K8sClusterInfo, manifest config.Manifest, timeout time.Duration) error {
8384
waitSet := util.NewWaitSet(timeout)
8485

85-
kubeConfigYaml, err := ioutil.ReadFile(kubeConfigPath)
86-
if err != nil {
87-
return err
88-
}
89-
9086
waits := manifest.Waits
91-
err = createByManifest(c, dc, manifest)
87+
err := createByManifest(c, manifest)
9288
if err != nil {
9389
return err
9490
}
@@ -103,7 +99,7 @@ func createManifestAndWait(c *kubernetes.Clientset, dc dynamic.Interface, manife
10399
wait := waits[idx]
104100
logger.Log.Infof("waiting for %+v", wait)
105101

106-
options, err := getWaitOptions(kubeConfigYaml, &wait)
102+
options, err := getWaitOptions(c, &wait)
107103
if err != nil {
108104
return err
109105
}
@@ -131,7 +127,7 @@ func createManifestAndWait(c *kubernetes.Clientset, dc dynamic.Interface, manife
131127
}
132128

133129
// RunCommandsAndWait Concurrently run commands and wait for conditions.
134-
func RunCommandsAndWait(run config.Run, timeout time.Duration) error {
130+
func RunCommandsAndWait(run config.Run, timeout time.Duration, cluster *util.K8sClusterInfo) error {
135131
waitSet := util.NewWaitSet(timeout)
136132

137133
commands := run.Command
@@ -140,7 +136,7 @@ func RunCommandsAndWait(run config.Run, timeout time.Duration) error {
140136
}
141137

142138
waitSet.WaitGroup.Add(1)
143-
go executeCommandsAndWait(commands, run.Waits, waitSet)
139+
go executeCommandsAndWait(commands, run.Waits, waitSet, cluster)
144140

145141
go func() {
146142
waitSet.WaitGroup.Wait()
@@ -160,7 +156,7 @@ func RunCommandsAndWait(run config.Run, timeout time.Duration) error {
160156
return nil
161157
}
162158

163-
func executeCommandsAndWait(commands string, waits []config.Wait, waitSet *util.WaitSet) {
159+
func executeCommandsAndWait(commands string, waits []config.Wait, waitSet *util.WaitSet, cluster *util.K8sClusterInfo) {
164160
defer waitSet.WaitGroup.Done()
165161

166162
// executes commands
@@ -177,13 +173,7 @@ func executeCommandsAndWait(commands string, waits []config.Wait, waitSet *util.
177173
wait := waits[idx]
178174
logger.Log.Infof("waiting for %+v", wait)
179175

180-
kubeConfigYaml, err := ioutil.ReadFile(kubeConfigPath)
181-
if err != nil {
182-
err = fmt.Errorf("read kube config failed: %s", err)
183-
waitSet.ErrChan <- err
184-
}
185-
186-
options, err := getWaitOptions(kubeConfigYaml, &wait)
176+
options, err := getWaitOptions(cluster, &wait)
187177
if err != nil {
188178
err = fmt.Errorf("commands: [%s] get wait options error: %s", commands, err)
189179
waitSet.ErrChan <- err
@@ -213,3 +203,13 @@ func GetIdentity() string {
213203
}
214204
return runID
215205
}
206+
207+
func InitLogFollower() {
208+
logFollower = util.NewResourceLogFollower(context.Background(), util.LogDir)
209+
}
210+
211+
func CloseLogFollower() {
212+
if logFollower != nil {
213+
logFollower.Close()
214+
}
215+
}

0 commit comments

Comments
 (0)