Skip to content

Commit 0c756f5

Browse files
authored
fix(webhook): transform bash/zsh commands to use sh executor (#165)
Fix for issue #164 by transforming container commands from 'bash -c "xx"' or 'zsh -c "xx"' to 'sh -c bash -c "xx"' or 'sh -c zsh -c "xx"' respectively. Closes #164
1 parent d2f92be commit 0c756f5

File tree

4 files changed

+82
-0
lines changed

4 files changed

+82
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/NexusGPU/tensor-fusion
33
go 1.24.1
44

55
require (
6+
al.essio.dev/pkg/shellescape v1.6.0
67
github.com/NVIDIA/go-nvml v0.12.4-1
78
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107
89
github.com/aws/aws-sdk-go-v2 v1.36.3

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=
2+
al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
13
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
24
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
35
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@@ -118,6 +120,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
118120
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
119121
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
120122
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
123+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
124+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
121125
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
122126
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
123127
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=

internal/webhook/v1/pod_webhook.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"sigs.k8s.io/controller-runtime/pkg/log"
3434
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3535

36+
"al.essio.dev/pkg/shellescape"
3637
tfv1 "github.com/NexusGPU/tensor-fusion/api/v1"
3738
"github.com/NexusGPU/tensor-fusion/internal/constants"
3839
"github.com/NexusGPU/tensor-fusion/internal/utils"
@@ -252,6 +253,16 @@ func (m *TensorFusionPodMutator) patchTFClient(
252253
for i := range pod.Spec.Containers {
253254
container := &pod.Spec.Containers[i]
254255
if container.Name == name {
256+
// fix for issue https://github.com/NexusGPU/tensor-fusion/issues/164:
257+
// transform bash/zsh -c commands
258+
if len(container.Command) >= 3 {
259+
shell := container.Command[0]
260+
if (shell == "bash" || shell == "zsh") && container.Command[1] == "-c" {
261+
originalCommand := container.Command[2]
262+
safeCommand := shellescape.Quote(originalCommand)
263+
container.Command = []string{"sh", "-c", fmt.Sprintf("%s -c %s", shell, safeCommand)}
264+
}
265+
}
255266
// patch from config
256267
containerJSON, err := json.Marshal(container)
257268
if err != nil {

internal/webhook/v1/pod_webhook_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,5 +555,71 @@ var _ = Describe("TensorFusionPodMutator", func() {
555555
// There should be at least 2 patches (initContainers and the container env patches)
556556
Expect(len(patch)).To(BeNumerically(">=", 2))
557557
})
558+
559+
It("should transform bash/zsh -c commands correctly", func() {
560+
// Create a pod with bash and zsh -c commands
561+
pod := &corev1.Pod{
562+
ObjectMeta: metav1.ObjectMeta{
563+
Name: "test-command-transform",
564+
Namespace: "default",
565+
},
566+
Spec: corev1.PodSpec{
567+
Containers: []corev1.Container{
568+
{
569+
Name: "bash-container",
570+
Image: "test-image",
571+
Command: []string{"bash", "-c", "echo 'hello world' && ls -la"},
572+
},
573+
{
574+
Name: "zsh-container",
575+
Image: "test-image",
576+
Command: []string{"zsh", "-c", "echo 'special chars: $HOME \"quoted\" text'"},
577+
},
578+
{
579+
Name: "other-container",
580+
Image: "test-image",
581+
Command: []string{"sh", "-c", "echo 'this should not change'"},
582+
},
583+
},
584+
},
585+
}
586+
587+
clientConfig := &tfv1.ClientConfig{}
588+
containerNames := []string{"bash-container", "zsh-container", "other-container"}
589+
nodeSelector := map[string]string{}
590+
591+
// Call the function that includes the command transformation
592+
mutator := &TensorFusionPodMutator{}
593+
patches, err := mutator.patchTFClient(pod, clientConfig, containerNames, nodeSelector)
594+
595+
// Verify results
596+
Expect(err).NotTo(HaveOccurred())
597+
598+
// Check that the patches include command transformations
599+
var bashCommandPatchFound, zshCommandPatchFound, otherCommandPatchFound bool
600+
601+
for _, patch := range patches {
602+
// Check for command transformation patches
603+
// Command patches are applied to individual elements, not the whole array
604+
if patch.Path == "/spec/containers/0/command/0" && patch.Value == "sh" {
605+
bashCommandPatchFound = true
606+
} else if patch.Path == "/spec/containers/0/command/2" {
607+
// Third element (the command string) for bash container
608+
Expect(patch.Value).To(ContainSubstring("bash -c"))
609+
} else if patch.Path == "/spec/containers/1/command/0" && patch.Value == "sh" {
610+
zshCommandPatchFound = true
611+
} else if patch.Path == "/spec/containers/1/command/2" {
612+
// Third element (the command string) for zsh container
613+
Expect(patch.Value).To(ContainSubstring("zsh -c"))
614+
} else if patch.Path == "/spec/containers/2/command/0" && patch.Value == "sh" {
615+
otherCommandPatchFound = true
616+
}
617+
}
618+
619+
// Verify the right patches were found
620+
Expect(bashCommandPatchFound).To(BeTrue(), "No patch found for bash command transformation")
621+
Expect(zshCommandPatchFound).To(BeTrue(), "No patch found for zsh command transformation")
622+
Expect(otherCommandPatchFound).To(BeFalse(), "Unexpected patch found for other container command transformation")
623+
})
558624
})
559625
})

0 commit comments

Comments
 (0)