Repository: google/gnxi (language: python)

Issue:
Username and password duplication.
It looks like the newer gnxi/gnmi_get commands are duplicating username and passwords.

**Older client:**
go run gnmi_get.go -target_addr localhost:6032  -username admin -password 123456! -v 10  -xpath "/local-routes"  --target_name localhost -insecure -time_out 120s
```
    "metadata": {
        "_internal_repr": {
            "user-agent": [
                "grpc-go/1.30.0"
            ],
            "password": [
                "123456!"
            ],
            "username": [
                "gnmi_admin"
            ]
        },
        "flags": 0
    },
```

**New client:**
```
    "metadata": {
        "_internal_repr": {
            "user-agent": [
                "grpc-go/1.34.0"
            ],
            "username": [
                "admin",
                "admin"
            ],
            "password": [
                "123456!",
                "123456!"
            ]
        },
        "flags": 0
    },
```
    
Looks like the commit is probably : https://github.com/google/gnxi/commit/ee650d60abdcfb4ad1759b311ad0450f332e1b05
    
Changing the following line to just `return metadata.AppendToOutgoingContext(ctx)` fixes the issue.
https://github.com/google/gnxi/blob/5f44df13a119bcc3748fa15b216c38bb9072a1d8/utils/credentials/credentials.go#L168

I'm not sure if there is any reason to be sending multiple username/passwords, or have username and passwords as lists instead of a string. 


    


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d8fe4fa7..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,1 +0,0 @@
-/.project
diff --git a/utils/credentials/credentials.go b/utils/credentials/credentials.go
index f53571fa..94d0da63 100644
--- a/utils/credentials/credentials.go
+++ b/utils/credentials/credentials.go
@@ -161,11 +161,19 @@ func ClientCredentials() []grpc.DialOption {
 }
 
 // AttachToContext attaches credentials to a context.
+// If there are existing credentials, it overrides their values.
 func AttachToContext(ctx context.Context) context.Context {
 	if authorizedUser.username == "" {
 		return ctx
 	}
-	return metadata.AppendToOutgoingContext(ctx, usernameKey, authorizedUser.username, passwordKey, authorizedUser.password)
+	md, ok := metadata.FromOutgoingContext(ctx)
+	if !ok {
+		md = metadata.MD{}
+	}
+	md.Set(usernameKey, authorizedUser.username)
+	md.Set(passwordKey, authorizedUser.password)
+
+	return metadata.NewOutgoingContext(ctx, md)
 }
 
 // GetCAEntity gets a CA entity from a CA file and private key.


Test patch:
diff --git a/utils/credentials/credentials_test.go b/utils/credentials/credentials_test.go
new file mode 100644
index 00000000..7547c162
--- /dev/null
+++ b/utils/credentials/credentials_test.go
@@ -0,0 +1,26 @@
+package credentials
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"golang.org/x/net/context"
+	"google.golang.org/grpc/metadata"
+)
+
+func TestTwiceAttachToContext(t *testing.T) {
+	authorizedUser = userCredentials{
+		username: "foo",
+		password: "bar",
+	}
+	ctx := AttachToContext(context.Background())
+	ctx = AttachToContext(ctx)
+	got, _ := metadata.FromOutgoingContext(ctx)
+	want := metadata.MD{
+		"username": []string{"foo"},
+		"password": []string{"bar"},
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Fatalf("(-got, +want):\n%s", diff)
+	}
+}