mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-18 22:52:11 +01:00
e65e55e2dd
### Describe Your Changes
These changes support using Azure Managed Identity for the `vmbackup`
utility. It adds two new environment variables:
* `AZURE_USE_DEFAULT_CREDENTIAL`: Instructs the `vmbackup` utility to
build a connection using the [Azure Default
Credential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity@v1.5.2#NewDefaultAzureCredential)
mode. This causes the Azure SDK to check for a variety of environment
variables to try and make a connection. By default, it tries to use
managed identity if that is set up.
This will close
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5984
### Checklist
The following checks are **mandatory**:
- [x] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).
### Testing
However you normally test the `vmbackup` utility using Azure Blob should
continue to work without any changes. The set up for that is environment
specific and not listed out here.
Once regression testing has been done you can set up [Azure Managed
Identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)
so your resource (AKS, VM, etc), can use that credential method. Once it
is set up, update your environment variables according to the updated
documentation.
I added unit tests to the `FS.Init` function, then made my changes, then
updated the unit tests to capture the new branches.
I tested this in our environment, but with SAS token auth and managed
identity and it works as expected.
---------
Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>
Co-authored-by: Justin Rush <jarush@epic.com>
Co-authored-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>
Co-authored-by: hagen1778 <roman@victoriametrics.com>
(cherry picked from commit 5fd3aef549
)
161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
package azremote
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
)
|
|
|
|
func Test_cleanDirectory(t *testing.T) {
|
|
cases := map[string]struct {
|
|
Dir string
|
|
ExpectedDir string
|
|
}{
|
|
"dir / prefix is removed": {
|
|
Dir: "/foo/",
|
|
ExpectedDir: "foo/",
|
|
},
|
|
"multiple dir prefix / is removed": {
|
|
Dir: "//foo/",
|
|
ExpectedDir: "foo/",
|
|
},
|
|
"suffix is added": {
|
|
Dir: "foo",
|
|
ExpectedDir: "foo/",
|
|
},
|
|
}
|
|
|
|
for name, test := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
dir := cleanDirectory(test.Dir)
|
|
|
|
if dir != test.ExpectedDir {
|
|
t.Errorf("expected dir %q, got %q", test.ExpectedDir, dir)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_FSInit(t *testing.T) {
|
|
cases := map[string]struct {
|
|
IgnoreFakeEnv bool
|
|
Env testEnv
|
|
ExpectedErr error
|
|
ExpectedLogs []string
|
|
}{
|
|
"connection string env var is used": {
|
|
Env: map[string]string{
|
|
envStorageAccCs: "BlobEndpoint=https://test.blob.core.windows.net/;SharedAccessSignature=",
|
|
},
|
|
ExpectedLogs: []string{`Creating AZBlob service client from connection string`},
|
|
},
|
|
"base envtemplate package is used and connection string err bubbles": {
|
|
IgnoreFakeEnv: true,
|
|
Env: map[string]string{
|
|
envStorageAccCs: "BlobEndpoint=https://test.blob.core.windows.net/;SharedAccessSignature=",
|
|
},
|
|
ExpectedErr: errNoCredentials,
|
|
},
|
|
"only storage account name is an err": {
|
|
Env: map[string]string{
|
|
envStorageAcctName: "test",
|
|
},
|
|
ExpectedErr: errNoCredentials,
|
|
},
|
|
"uses shared key credential": {
|
|
Env: map[string]string{
|
|
envStorageAcctName: "test",
|
|
envStorageAccKey: "dGVhcG90Cg==",
|
|
},
|
|
ExpectedLogs: []string{`Creating AZBlob service client from account name and key`},
|
|
},
|
|
"allows overriding domain name with account name and key": {
|
|
Env: map[string]string{
|
|
envStorageAcctName: "test",
|
|
envStorageAccKey: "dGVhcG90Cg==",
|
|
envStorageDomain: "foo.bar",
|
|
},
|
|
ExpectedLogs: []string{
|
|
`Creating AZBlob service client from account name and key`,
|
|
`Overriding default Azure blob domain with "foo.bar"`,
|
|
},
|
|
},
|
|
"can't specify both connection string and shared key": {
|
|
Env: map[string]string{
|
|
envStorageAccCs: "teapot",
|
|
envStorageAcctName: "test",
|
|
envStorageAccKey: "dGVhcG90Cg==",
|
|
},
|
|
ExpectedErr: errInvalidCredentials,
|
|
},
|
|
"just use default is an err": {
|
|
Env: map[string]string{
|
|
envStorageDefault: "true",
|
|
},
|
|
ExpectedErr: errNoCredentials,
|
|
},
|
|
"uses default credential": {
|
|
Env: map[string]string{
|
|
envStorageDefault: "true",
|
|
envStorageAcctName: "test",
|
|
},
|
|
ExpectedLogs: []string{`Creating AZBlob service client from default credential`},
|
|
},
|
|
}
|
|
|
|
for name, test := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
tlog := &testLogger{}
|
|
|
|
logger.SetOutputForTests(tlog)
|
|
t.Cleanup(logger.ResetOutputForTest)
|
|
|
|
fs := &FS{Dir: "foo"}
|
|
if test.Env != nil && !test.IgnoreFakeEnv {
|
|
fs.env = test.Env.LookupEnv
|
|
}
|
|
|
|
err := fs.Init()
|
|
if err != nil && !errors.Is(err, test.ExpectedErr) {
|
|
t.Errorf("expected error %q, got %q", test.ExpectedErr, err)
|
|
}
|
|
|
|
tlog.MustContain(t, test.ExpectedLogs...)
|
|
})
|
|
}
|
|
}
|
|
|
|
type testLogger struct {
|
|
buf *bytes.Buffer
|
|
}
|
|
|
|
func (l *testLogger) Write(p []byte) (n int, err error) {
|
|
if l.buf == nil {
|
|
l.buf = &bytes.Buffer{}
|
|
}
|
|
|
|
return l.buf.Write(p)
|
|
}
|
|
|
|
func (l *testLogger) MustContain(t *testing.T, vals ...string) {
|
|
t.Helper()
|
|
|
|
contents := l.buf.String()
|
|
|
|
for _, val := range vals {
|
|
if !strings.Contains(contents, val) {
|
|
t.Errorf("expected log to contain %q, got %q", val, l.buf.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
type testEnv map[string]string
|
|
|
|
func (e testEnv) LookupEnv(key string) (string, bool) {
|
|
val, ok := e[key]
|
|
return val, ok
|
|
}
|