Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
Signed-off-by: Hang Yan <[email protected]>
  • Loading branch information
hangyan committed Dec 16, 2024
1 parent 4092ff6 commit 4d58ac0
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 54 deletions.
112 changes: 63 additions & 49 deletions pkg/antctl/raw/supportbundle/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ func controllerRemoteRunE(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to create clientset: %w", err)
}

if err := os.MkdirAll(option.dir, 0700|os.ModeDir); err != nil {
if err := os.MkdirAll(option.dir, 0700); err != nil {
return fmt.Errorf("error when creating output dir: %w", err)
}

Expand Down Expand Up @@ -688,8 +688,8 @@ func processResults(ctx context.Context, antreaClientset antrea.Interface, k8sCl
}
}

controllerFail := resultMap[""] != nil
if controllerFail {
controllerFailed := resultMap[""] != nil
if controllerFailed {
fmt.Println("Controller Info Failed Reason: " + resultMap[""].Error())
}

Expand All @@ -704,15 +704,15 @@ func processResults(ctx context.Context, antreaClientset antrea.Interface, k8sCl

// download logs from kubernetes api
if failedNodes != nil {
err = downloadAgentBundleFromKubernetes(ctx, antreaClientset, k8sClient, failedNodes, dir)
err = downloadFallbackAgentBundleFromKubernetes(ctx, antreaClientset, k8sClient, failedNodes, dir)
if err != nil {
fmt.Println("Failed to download agent bundle from kubernetes api: " + err.Error())
} else {
allFailed = false
}
}
if controllerFail {
err = downloadControlleBundleFromKubernetes(ctx, antreaClientset, k8sClient, dir)
if controllerFailed {
err = downloadFallbackControllerBundleFromKubernetes(ctx, antreaClientset, k8sClient, dir)
if err != nil {
fmt.Println("Failed to download controller bundle from kubernetes api: " + err.Error())
} else {
Expand All @@ -727,38 +727,44 @@ func processResults(ctx context.Context, antreaClientset antrea.Interface, k8sCl
}
}

func downloadControlleBundleFromKubernetes(ctx context.Context, antreaClientset antrea.Interface, k8sClient kubernetes.Interface, dir string) error {
func downloadFallbackControllerBundleFromKubernetes(ctx context.Context, antreaClientset antrea.Interface, k8sClient kubernetes.Interface, dir string) error {
var errors []error
tmpDir, err := afero.TempDir(defaultFS, "", "bundle_tmp_")
if err != nil {
return err
}
defer defaultFS.RemoveAll(tmpDir)

controllerInfo, err := antreaClientset.CrdV1beta1().AntreaControllerInfos().Get(ctx, "antrea-controller", metav1.GetOptions{})
if err == nil {
var podRef *corev1.ObjectReference
if err := func() error {
controllerInfo, err := antreaClientset.CrdV1beta1().AntreaControllerInfos().Get(ctx, v1beta1.AntreaControllerInfoResourceName, metav1.GetOptions{})
if err != nil {
return err
}
podRef = &controllerInfo.PodRef
data, err := yaml.Marshal(controllerInfo)
if err == nil {
err = afero.WriteFile(defaultFS, filepath.Join(tmpDir, "controllerinfo"), data, 0644)
errors = append(errors, err)
if err != nil {
return err
}
if err := afero.WriteFile(defaultFS, filepath.Join(dir, "controllerinfo"), data, 0644); err != nil {
return err
}
return nil
}(); err != nil {
errors = append(errors, err)
}
errors = append(errors, err)
pods, err := k8sClient.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{
ResourceVersion: "0",
LabelSelector: "app=antrea,component=antrea-controller",
})
errors = append(errors, err)
if err == nil {
for _, pod := range pods.Items {
err = downloadPodLogs(ctx, k8sClient, "controller", pod.Namespace, pod.Name, k8s.GetPodContainerNames(&pod), dir, tmpDir)
if podRef != nil {
pod, err := k8sClient.CoreV1().Pods(podRef.Namespace).Get(ctx, podRef.Name, metav1.GetOptions{})
if err == nil {
err = downloadAndPackPodLogs(ctx, k8sClient, pod, dir, tmpDir)
errors = append(errors, err)
}
}
return utilerror.NewAggregate(errors)
}

func downloadAgentBundleFromKubernetes(ctx context.Context, antreaClientset antrea.Interface, k8sClient kubernetes.Interface, failedNodes []string, dir string) error {
func downloadFallbackAgentBundleFromKubernetes(ctx context.Context, antreaClientset antrea.Interface, k8sClient kubernetes.Interface, failedNodes []string, dir string) error {
agentInfoList, err := antreaClientset.CrdV1beta1().AntreaAgentInfos().List(ctx, metav1.ListOptions{ResourceVersion: "0"})
if err != nil {
return err
Expand Down Expand Up @@ -796,31 +802,45 @@ func downloadAgentBundleFromKubernetes(ctx context.Context, antreaClientset antr
errors = append(errors, err)
}
}
err = downloadPodLogs(ctx, k8sClient, "agent_"+pod.Spec.NodeName, pod.Namespace, pod.Name, k8s.GetPodContainerNames(&pod), dir, tmpDir)
err = downloadAndPackPodLogs(ctx, k8sClient, &pod, dir, tmpDir)
errors = append(errors, err)

}
return utilerror.NewAggregate(errors)
}

func downloadPodLogs(ctx context.Context, k8sClient kubernetes.Interface, comp string, namespace string, podName string, containers []string, dir string, tmpDir string) error {
var errors []error
for _, containerName := range containers {
containerDirName := containerName
if strings.HasPrefix(containerName, "antrea-") {
containerDirName = strings.ReplaceAll(containerName, "antrea-", "")
}
// downloadAndPackPodLogs download pod's logs and compress them to the target dir. `tmpDir` is used to store the logs file momentarily.
func downloadAndPackPodLogs(ctx context.Context, k8sClient kubernetes.Interface, pod *corev1.Pod, dir string, tmpDir string) error {
err := downloadPodLogs(ctx, k8sClient, pod.Namespace, pod.Name, k8s.GetPodContainerNames(pod), tmpDir)
if err != nil {
return err
}
prefix := "agent_"
if strings.Contains(pod.Name, "controller") {
prefix = "controller_"
}
gzFileName := filepath.Join(dir, prefix+pod.Spec.NodeName+".tar.gz")
f, err := defaultFS.Create(gzFileName)
if err != nil {
return err
} else {
defer f.Close()
_, err := compress.PackDir(defaultFS, tmpDir, f)
return err
}
}

podLogDir := filepath.Join(tmpDir, "logs", containerDirName)
err := os.MkdirAll(podLogDir, 0755)
func downloadPodLogs(ctx context.Context, k8sClient kubernetes.Interface, namespace string, podName string, containers []string, tmpDir string) error {
downloadContainerLogs := func(containerName string) error {
containerDirName, _ := strings.CutPrefix(containerName, "antrea-")
containerLogDir := filepath.Join(tmpDir, "logs", containerDirName)
err := os.MkdirAll(containerLogDir, 0755)
if err != nil {
return err
}
fileName := filepath.Join(podLogDir, containerName+".log")
fileName := filepath.Join(containerLogDir, containerName+".log")
f, err := defaultFS.Create(fileName)
if err != nil {
errors = append(errors, err)
continue
return err
}
defer f.Close()
logOption := &corev1.PodLogOptions{
Expand All @@ -829,24 +849,18 @@ func downloadPodLogs(ctx context.Context, k8sClient kubernetes.Interface, comp s
logs := k8sClient.CoreV1().Pods(namespace).GetLogs(podName, logOption)
logStream, err := logs.Stream(ctx)
if err != nil {
errors = append(errors, err)
continue
return err
}

_, err = io.Copy(f, logStream)
errors = append(errors, err)
err = logStream.Close()
errors = append(errors, err)
if err != nil {
return err
}
return logStream.Close()
}

gzFileName := filepath.Join(dir, comp+".tar.gz")
f, err := defaultFS.Create(gzFileName)
if err != nil {
errors = append(errors, err)
} else {
defer f.Close()
_, err := compress.PackDir(defaultFS, tmpDir, f)
errors = append(errors, err)
var errors []error
for _, containerName := range containers {
errors = append(errors, downloadContainerLogs(containerName))
}
return utilerror.NewAggregate(errors)
}
25 changes: 21 additions & 4 deletions pkg/antctl/raw/supportbundle/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
fakeclientset "antrea.io/antrea/pkg/client/clientset/versioned/fake"
"antrea.io/antrea/pkg/client/clientset/versioned/scheme"
systemclientset "antrea.io/antrea/pkg/client/clientset/versioned/typed/system/v1beta1"
"antrea.io/antrea/pkg/util/compress"
)

var (
Expand All @@ -59,6 +60,10 @@ var (
Kind: "Node",
Name: "node-1",
},
PodRef: v1.ObjectReference{
Name: "antrea-controller-1",
Namespace: "kube-system",
},
}
node1 = v1.Node{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -411,14 +416,26 @@ func TestProcessResults(t *testing.T) {
require.NoError(t, err)
data := string(b)
for node, err := range tt.resultMap {
fileName := fmt.Sprintf("agent_%s.tar.gz", node)
tgzFileName := fmt.Sprintf("agent_%s.tar.gz", node)
if node == "" {
fileName = "controller.tar.gz"
tgzFileName = "controller_node-1.tar.gz"
}
if err != nil {
ok, checkErr := afero.Exists(defaultFS, filepath.Join(option.dir, fileName))
ok, checkErr := afero.Exists(defaultFS, filepath.Join(option.dir, tgzFileName))
require.NoError(t, checkErr)
assert.True(t, ok, "expected support bundle file %s not found", tgzFileName)

unpackError := compress.UnpackDir(defaultFS, filepath.Join(option.dir, tgzFileName), option.dir)
require.NoError(t, unpackError)
expectFileName := "logs/agent/antrea-agent.log"
if node == "" {
expectFileName = "logs/controller/antrea-controller.log"
}
ok, checkErr = afero.Exists(defaultFS, filepath.Join(option.dir, expectFileName))
require.NoError(t, checkErr)
assert.True(t, ok, fmt.Sprintf("expected support bundle file %s not found", fileName))
assert.True(t, ok, "expected log file %s not found", expectFileName)
deleteErr := defaultFS.Remove(filepath.Join(option.dir, expectFileName))
require.NoError(t, deleteErr)
}

if node == "" {
Expand Down
65 changes: 65 additions & 0 deletions pkg/util/compress/compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"archive/tar"
"compress/gzip"
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
"path/filepath"
Expand All @@ -26,6 +28,69 @@ import (
"github.com/spf13/afero"
)

// Sanitize archive file pathing from "G305: Zip Slip vulnerability"
func sanitizeArchivePath(d, t string) (v string, err error) {
v = filepath.Join(d, t)
if strings.HasPrefix(v, filepath.Clean(d)) {
return v, nil
}
return "", fmt.Errorf("%s: %s", "content filepath is tainted", t)
}

func UnpackDir(fs afero.Fs, fileName string, targetDir string) error {
file, err := fs.Open(fileName)
if err != nil {
return err
}
defer file.Close()

reader, err := gzip.NewReader(file)
if err != nil {
return err
}
defer reader.Close()
tarReader := tar.NewReader(reader)

for true {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
targetPath, err := sanitizeArchivePath(targetDir, header.Name)
if err != nil {
return err
}
switch header.Typeflag {
case tar.TypeDir:
if err := fs.Mkdir(targetPath, 0755); err != nil {
return err
}
case tar.TypeReg:
outFile, err := fs.Create(targetPath)
defer outFile.Close()
if err != nil {
return err
}
for {
// to resolve G110: Potential DoS vulnerability via decompression bomb
_, err := io.CopyN(outFile, tarReader, 1024)
if err != nil {
if err == io.EOF {
break
}
return err
}
}
default:
return errors.New("unknown type found when reading tgz file")
}
}
return nil
}

func PackDir(fs afero.Fs, dir string, writer io.Writer) ([]byte, error) {
hash := sha256.New()
gzWriter := gzip.NewWriter(io.MultiWriter(hash, writer))
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/k8s/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func IsPodTerminated(pod *v1.Pod) bool {
return pod.Status.Phase == v1.PodFailed || pod.Status.Phase == v1.PodSucceeded
}

// GetPodContainersNames return all the container names in a pod, including init container.
// GetPodContainersNames return all the container names in a Pod, including init container.
func GetPodContainerNames(pod *v1.Pod) []string {
var names []string
for _, c := range pod.Spec.InitContainers {
Expand Down

0 comments on commit 4d58ac0

Please sign in to comment.