Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,4 @@ delete-metrics-svc:

.PHONY: blobfuse-proxy
blobfuse-proxy:
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -mod vendor -ldflags="-s -w" -o _output/${ARCH}/blobfuse-proxy ./pkg/blobfuse-proxy
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -mod vendor -ldflags="-s -w -X ${PKG}/pkg/blobfuse-proxy/server.driverVersion=${IMAGE_VERSION}" -o _output/${ARCH}/blobfuse-proxy ./pkg/blobfuse-proxy
28 changes: 25 additions & 3 deletions pkg/blobfuse-proxy/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ import (
)

var (
mutex sync.Mutex
mutex sync.Mutex
driverVersion string
)

// telemetryTagPrefix is used to identify the mounts done via blobcsi driver
const telemetryTagPrefix = "blobpartner-csi/"
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The telemetry tag prefix is inconsistent with the PR description. The PR description states the telemetry tag should be "azpartner-aks/", but the implementation uses "blobpartner-csi/". This mismatch needs to be corrected to match the intended telemetry format.

Suggested change
const telemetryTagPrefix = "blobpartner-csi/"
const telemetryTagPrefix = "azpartner-aks/"

Copilot uses AI. Check for mistakes.

type BlobfuseVersion int

const (
Expand All @@ -47,12 +51,14 @@ const (
type MountServer struct {
blobfuseVersion BlobfuseVersion
mount_azure_blob.UnimplementedMountServiceServer
exec func(name string, arg ...string) *exec.Cmd
}

// NewMountServer returns a new Mountserver
func NewMountServiceServer() *MountServer {
mountServer := &MountServer{}
mountServer.blobfuseVersion = getBlobfuseVersion()
mountServer.exec = exec.Command
return mountServer
}

Expand All @@ -71,6 +77,7 @@ func (server *MountServer) MountAzureBlob(_ context.Context,
var cmd *exec.Cmd
var result mount_azure_blob.MountAzureBlobResponse
if protocol == blob.Fuse2 || server.blobfuseVersion == BlobfuseV2 {
telemetryTag := telemetryTagPrefix + driverVersion
args = "mount " + args
// add this arg for blobfuse2 to solve the issue:
// https://github.com/Azure/azure-storage-fuse/issues/1015
Expand All @@ -82,13 +89,28 @@ func (server *MountServer) MountAzureBlob(_ context.Context,
klog.V(2).Infof("append --disable-version-check to mount args")
args = args + " " + "--disable-version-check=true"
}
// Adding telemetry tag to know that blob is been mounted through AKS CSI Driver
if !strings.Contains(args, "--telemetry") {
klog.V(2).Infof("append --telemetry=%s to mount args", telemetryTag)
args = args + " " + "--telemetry=" + telemetryTag
} else {
// If telemetry flag is already present, check for aks tag if not present
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If telemetry flag is already present, we just respect original setting, I don't think we need to change the original value by force

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If user has some custom tag, then we can't use that for tracking blob usage through AKS. If we just use the original setting then whole purpose to track usage will be lost, so I appended our tag with user specified value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but what if customer only wants to keep the original telemetry? this PR would change the telemetry even user does not want to.

and this adds the telemetry as azpartner-aks in open source driver, which means if it runs on AWS k8s node, it would still have this azpartner-aks telemetry, so maybe use default userAgent as fmt.Sprintf("%s/%s", driverName, driverVersion) is more reasonable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated tag to blobpartner-csi/<driver-version>, Concern from blobfuse team is if we go with driver specific tag then it will become costly operation for blobfuse analytics team to parse these tags (since they are expecting all partners should use similar tag instead of each partner goes with different tag).

// then user might have their own telemetry tag append aks tag to it
if !strings.Contains(args, telemetryTagPrefix) {
splitedArgs := strings.Split(args, "--telemetry=")
if len(splitedArgs) == 2 {
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1]
Comment on lines +100 to +102
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'splitedArgs' should be 'splitArgs' or 'splittedArgs'. 'Splited' is not a valid English word; the past tense of 'split' is 'split' or 'splitted' (though 'split' is more common).

Suggested change
splitedArgs := strings.Split(args, "--telemetry=")
if len(splitedArgs) == 2 {
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1]
splitArgs := strings.Split(args, "--telemetry=")
if len(splitArgs) == 2 {
args = splitArgs[0] + " --telemetry=" + telemetryTag + "," + splitArgs[1]

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string manipulation logic for inserting the telemetry tag creates a potential issue with spacing. The code constructs splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1], which adds a space before --telemetry= but doesn't trim it from splitedArgs[0]. This could result in double spaces if splitedArgs[0] already ends with a space. Consider using strings.TrimSpace() on splitedArgs[0] before concatenation.

Suggested change
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1]
args = strings.TrimSpace(splitedArgs[0]) + " --telemetry=" + telemetryTag + "," + splitedArgs[1]

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic doesn't handle the edge case where --telemetry= is provided with an empty value (e.g., --telemetry= --other-flag). In this case, splitedArgs[1] would start with a space and a different flag, resulting in malformed telemetry arguments like --telemetry=blobpartner-csi/version, --other-flag. Consider trimming whitespace from splitedArgs[1] and checking if it's empty before concatenating.

Suggested change
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1]
telemetryValue := strings.TrimSpace(splitedArgs[1])
if telemetryValue == "" {
args = splitedArgs[0] + " --telemetry=" + telemetryTag
} else {
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + telemetryValue
}

Copilot uses AI. Check for mistakes.
}
klog.V(2).Infof("updated --telemetry tag in mount args: %s", args)
Comment on lines +100 to +104
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The telemetry string parsing logic has a critical flaw. When splitting by "--telemetry=", if there are multiple occurrences of this substring in args (e.g., in a container name or account name), len(splitedArgs) could be greater than 2, but only the first occurrence is handled. This could result in malformed arguments. Consider using a more robust parsing method such as regular expressions or proper argument parsing to specifically target the --telemetry flag.

Suggested change
splitedArgs := strings.Split(args, "--telemetry=")
if len(splitedArgs) == 2 {
args = splitedArgs[0] + " --telemetry=" + telemetryTag + "," + splitedArgs[1]
}
klog.V(2).Infof("updated --telemetry tag in mount args: %s", args)
// Split args into slice for robust parsing
argSlice := strings.Fields(args)
telemetryIdx := -1
for i, arg := range argSlice {
if strings.HasPrefix(arg, "--telemetry=") {
telemetryIdx = i
break
}
}
if telemetryIdx != -1 {
// Update the telemetry tag, appending our tag if not present
telemetryVal := strings.TrimPrefix(argSlice[telemetryIdx], "--telemetry=")
// Avoid duplicate tags
if !strings.Contains(telemetryVal, telemetryTagPrefix) {
argSlice[telemetryIdx] = "--telemetry=" + telemetryTag + "," + telemetryVal
args = strings.Join(argSlice, " ")
klog.V(2).Infof("updated --telemetry tag in mount args: %s", args)
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +104
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code silently does nothing when len(splitedArgs) != 2 (line 101 condition). This can occur if the string "--telemetry=" appears multiple times or is part of another parameter value. Consider adding logging (e.g., klog.Warningf) to indicate when the telemetry tag cannot be updated, so operators can identify potential issues with their mount arguments.

Suggested change
}
klog.V(2).Infof("updated --telemetry tag in mount args: %s", args)
klog.V(2).Infof("updated --telemetry tag in mount args: %s", args)
} else {
klog.Warningf("could not update --telemetry tag in mount args: unexpected format or multiple --telemetry= flags found in args: %s", args)
}

Copilot uses AI. Check for mistakes.
}
}
args = util.TrimDuplicatedSpace(args)
klog.V(2).Infof("mount with v2, protocol: %s, args: %s", protocol, args)
cmd = exec.Command("blobfuse2", strings.Split(args, " ")...)
cmd = server.exec("blobfuse2", strings.Split(args, " ")...)
} else {
args = util.TrimDuplicatedSpace(args)
klog.V(2).Infof("mount with v1, protocol: %s, args: %s", protocol, args)
cmd = exec.Command("blobfuse", strings.Split(args, " ")...)
cmd = server.exec("blobfuse", strings.Split(args, " ")...)
}

cmd.Env = append(os.Environ(), authEnv...)
Expand Down
94 changes: 94 additions & 0 deletions pkg/blobfuse-proxy/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package server

import (
"context"
"os/exec"
"strings"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -64,3 +66,95 @@ func TestServerMountAzureBlob(t *testing.T) {
})
}
}

// fakeExecCommand is used to mock exec.Command for testing, it returns list of args
func fakeExecCommandEchoArgs(_ string, args ...string) *exec.Cmd {
return exec.Command("echo", append([]string{"-n"}, args...)...)
}

func TestServerMountAzureBlob_Telemetry(t *testing.T) {
driverVersion = "fake-version"
t.Parallel()
testCases := []struct {
name string
args string
code codes.Code
mountServer MountServer
areValidTelemetryArgs func(cmdArgs string) bool
}{
{
name: "mount_with_telemetry_tag_blobfusev2",
args: "--account-name=testaccount --container-name=testcontainer --telemetry=volume1-app1 --tmp-path=/tmp/blobfuse-tmp",
mountServer: MountServer{
blobfuseVersion: BlobfuseV2,
exec: fakeExecCommandEchoArgs,
},
code: codes.OK,
areValidTelemetryArgs: func(cmdArgs string) bool {
expectedTelemetryArg := "--telemetry=" + telemetryTagPrefix + driverVersion + ",volume1-app1"
return strings.Contains(cmdArgs, expectedTelemetryArg)
},
},
{
name: "mount_without_telemetry_tag_blobfusev2",
args: "--account-name=testaccount --container-name=testcontainer --tmp-path=/tmp/blobfuse-tmp",
mountServer: MountServer{
blobfuseVersion: BlobfuseV2,
exec: fakeExecCommandEchoArgs,
},
code: codes.OK,
areValidTelemetryArgs: func(cmdArgs string) bool {
expectedTelemetryArg := "--telemetry=" + telemetryTagPrefix + driverVersion
return strings.Contains(cmdArgs, expectedTelemetryArg)
},
},
{
name: "mount_with_same_telemetry_tag_blobfusev2",
args: "--account-name=testaccount --container-name=testcontainer --telemetry=" + telemetryTagPrefix + driverVersion,
mountServer: MountServer{
blobfuseVersion: BlobfuseV2,
exec: fakeExecCommandEchoArgs,
},
code: codes.OK,
areValidTelemetryArgs: func(cmdArgs string) bool {
// Argument order should remain unchanged
return strings.Contains(cmdArgs, "--account-name=testaccount --container-name=testcontainer --telemetry="+telemetryTagPrefix+driverVersion)
},
},
{
name: "mount_with_blobfusev1",
args: "--account-name=testaccount --container-name=testcontainer --tmp-path=/tmp/blobfuse-tmp",
mountServer: MountServer{
blobfuseVersion: BlobfuseV1,
exec: fakeExecCommandEchoArgs,
},
code: codes.OK,
areValidTelemetryArgs: func(cmdArgs string) bool {
// No telemetry arg should be added for blobfuse v1
return !strings.Contains(cmdArgs, "--telemetry=") && cmdArgs == "--account-name=testaccount --container-name=testcontainer --tmp-path=/tmp/blobfuse-tmp"
},
},
Comment on lines +100 to +136
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test case: The telemetry string manipulation logic in server.go lines 100-103 doesn't have a test case for when len(splitedArgs) != 2 (e.g., when "--telemetry=" appears multiple times in args or not at all in the else branch). This edge case should be tested to ensure the code handles malformed input gracefully without modifying args or causing unexpected behavior.

Copilot uses AI. Check for mistakes.
}

for i := range testCases {
tc := testCases[i]

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

req := mount_azure_blob.MountAzureBlobRequest{
MountArgs: tc.args,
AuthEnv: []string{},
}
res, err := tc.mountServer.MountAzureBlob(context.Background(), &req)
if tc.code == codes.OK {
require.NoError(t, err)
require.NotNil(t, res)
require.True(t, tc.areValidTelemetryArgs(res.Output), "telemetry args are not valid in command args: %s", res.Output)
} else {
require.Error(t, err)
require.NotNil(t, res)
}
})
}
}
Loading