Skip to content

Commit 757be60

Browse files
authored
Improve azure provider (tellerops#99)
Improve azure provider
1 parent 65612ac commit 757be60

File tree

5 files changed

+243
-14
lines changed

5 files changed

+243
-14
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mocks:
1717
mockgen -source pkg/providers/onepassword.go -destination pkg/providers/mock_providers/onepassword_mock.go
1818
mockgen -source pkg/providers/gopass.go -destination pkg/providers/mock_providers/gopass_mock.go
1919
mockgen -source pkg/providers/github.go -destination pkg/providers/mock_providers/github_mock.go
20+
mockgen -source pkg/providers/azure_keyvault.go -destination pkg/providers/mock_providers/azure_keyvault_mock.go
2021
readme:
2122
yarn readme
2223
lint:

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,35 @@ providers:
11251125
path: /folder/bar
11261126
```
11271127

1128+
## Azure
1129+
1130+
### Authentication
1131+
1132+
Two options for getting Azure authentication are available:
1133+
1. Standard Azure [environment variable](https://docs.microsoft.com/en-us/azure/developer/go/azure-sdk-authorization). (Enable by default)
1134+
2. For get credentials via [az client](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) add `AZURE_CLI=1` to your environment variable
1135+
1136+
Then set your vault-name by specific as environment variable: `KVAULT_NAME`
1137+
1138+
### Features
1139+
1140+
* Sync - `yes`
1141+
* Mapping - `yes`
1142+
* Modes - `read+write+delete`
1143+
1144+
### Example Config
1145+
1146+
```yaml
1147+
providers:
1148+
azure_keyvault:
1149+
env_sync:
1150+
path: azure
1151+
env:
1152+
FOO:
1153+
path: bar
1154+
```
1155+
1156+
11281157
# Semantics
11291158

11301159
## Addressing

pkg/providers/azure_keyvault.go

+48-14
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@ import (
88

99
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
1010
kvauth "github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
11+
"github.com/Azure/go-autorest/autorest"
1112
"github.com/spectralops/teller/pkg/core"
1213
"github.com/spectralops/teller/pkg/logging"
1314
)
1415

1516
const AzureVaultDomain = "vault.azure.net"
1617

18+
type AzureKeyVaultClient interface {
19+
SetSecret(ctx context.Context, vaultBaseURL string, secretName string, parameters keyvault.SecretSetParameters) (result keyvault.SecretBundle, err error)
20+
GetSecret(ctx context.Context, vaultBaseURL string, secretName string, secretVersion string) (result keyvault.SecretBundle, err error)
21+
GetSecrets(ctx context.Context, vaultBaseURL string, maxresults *int32) (result keyvault.SecretListResultPage, err error)
22+
DeleteSecret(ctx context.Context, vaultBaseURL string, secretName string) (result keyvault.DeletedSecretBundle, err error)
23+
}
24+
1725
type AzureKeyVault struct {
18-
client *keyvault.BaseClient
19-
logger logging.Logger
20-
vaultName string
26+
client AzureKeyVaultClient
27+
logger logging.Logger
28+
vaultName string
29+
vaultBaseURL string
2130
}
2231

2332
func NewAzureKeyVault(logger logging.Logger) (core.Provider, error) {
@@ -26,31 +35,56 @@ func NewAzureKeyVault(logger logging.Logger) (core.Provider, error) {
2635
return nil, fmt.Errorf("cannot find KVAULT_NAME for azure key vault")
2736
}
2837

29-
authorizer, err := kvauth.NewAuthorizerFromEnvironment()
38+
var authorizer autorest.Authorizer
39+
var err error
40+
41+
if _, ok := os.LookupEnv("AZURE_CLI"); ok {
42+
authorizer, err = kvauth.NewAuthorizerFromCLI()
43+
} else {
44+
authorizer, err = kvauth.NewAuthorizerFromEnvironment()
45+
}
46+
3047
if err != nil {
3148
return nil, err
3249
}
3350

3451
basicClient := keyvault.New()
3552
basicClient.Authorizer = authorizer
36-
return &AzureKeyVault{client: &basicClient, vaultName: vaultName, logger: logger}, nil
53+
return &AzureKeyVault{client: &basicClient,
54+
vaultName: vaultName,
55+
logger: logger,
56+
vaultBaseURL: "https://" + vaultName + "." + AzureVaultDomain,
57+
}, nil
3758
}
3859

3960
func (a *AzureKeyVault) Name() string {
4061
return "azure_keyvault"
4162
}
63+
4264
func (a *AzureKeyVault) Put(p core.KeyPath, val string) error {
43-
return fmt.Errorf("provider %q does not implement write yet", a.Name())
65+
a.logger.WithField("path", p.Path).Debug("set secret")
66+
_, err := a.client.SetSecret(context.TODO(), a.vaultBaseURL, p.Path, keyvault.SecretSetParameters{
67+
Value: &val,
68+
})
69+
return err
4470
}
71+
4572
func (a *AzureKeyVault) PutMapping(p core.KeyPath, m map[string]string) error {
46-
return fmt.Errorf("provider %q does not implement write yet", a.Name())
73+
for k, v := range m {
74+
ap := p.SwitchPath(k)
75+
err := a.Put(ap, v)
76+
if err != nil {
77+
return err
78+
}
79+
}
80+
return nil
4781
}
82+
4883
func (a *AzureKeyVault) GetMapping(kp core.KeyPath) ([]core.EnvEntry, error) {
4984
r := []core.EnvEntry{}
5085
ctx := context.Background()
51-
vaultBaseURL := "https://" + a.vaultName + "." + AzureVaultDomain
52-
a.logger.WithField("vault_base_url", vaultBaseURL).Debug("get secrets")
53-
secretList, err := a.client.GetSecrets(ctx, vaultBaseURL, nil)
86+
a.logger.WithField("vault_base_url", a.vaultBaseURL).Debug("get secrets")
87+
secretList, err := a.client.GetSecrets(ctx, a.vaultBaseURL, nil)
5488
if err != nil {
5589
return nil, err
5690
}
@@ -91,18 +125,18 @@ func (a *AzureKeyVault) Get(p core.KeyPath) (*core.EnvEntry, error) {
91125
}
92126

93127
func (a *AzureKeyVault) Delete(kp core.KeyPath) error {
94-
return fmt.Errorf("%s does not implement delete yet", a.Name())
128+
_, err := a.client.DeleteSecret(context.TODO(), a.vaultBaseURL, kp.Path)
129+
return err
95130
}
96131

97132
func (a *AzureKeyVault) DeleteMapping(kp core.KeyPath) error {
98133
return fmt.Errorf("%s does not implement delete yet", a.Name())
99134
}
100135

101136
func (a *AzureKeyVault) getSecret(kp core.KeyPath) (keyvault.SecretBundle, error) {
102-
vaultBaseURL := "https://" + a.vaultName + "." + AzureVaultDomain
103137
a.logger.WithFields(map[string]interface{}{
104-
"vault_base_url": vaultBaseURL,
138+
"vault_base_url": a.vaultBaseURL,
105139
"secret_name": kp.Path,
106140
}).Debug("get secret")
107-
return a.client.GetSecret(context.Background(), vaultBaseURL, kp.Path, "")
141+
return a.client.GetSecret(context.Background(), a.vaultBaseURL, kp.Path, "")
108142
}

pkg/providers/azure_keyvault_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
9+
"github.com/alecthomas/assert"
10+
"github.com/golang/mock/gomock"
11+
12+
"github.com/spectralops/teller/pkg/core"
13+
"github.com/spectralops/teller/pkg/providers/mock_providers"
14+
)
15+
16+
func String(v string) *string { return &v }
17+
18+
func TestAzureKeyVault(t *testing.T) {
19+
20+
ctrl := gomock.NewController(t)
21+
defer ctrl.Finish()
22+
client := mock_providers.NewMockAzureKeyVaultClient(ctrl)
23+
24+
a := AzureKeyVault{
25+
client: client,
26+
logger: GetTestLogger(),
27+
vaultName: "test",
28+
vaultBaseURL: "https://test/",
29+
}
30+
31+
path := "settings/prod/billing-svc"
32+
shazam := "shazam"
33+
34+
secretList := keyvault.SecretListResult{
35+
Value: &[]keyvault.SecretItem{
36+
{ID: String("all")},
37+
{ID: String("all-1")},
38+
},
39+
}
40+
41+
stopNext := func(context.Context, keyvault.SecretListResult) (keyvault.SecretListResult, error) {
42+
return keyvault.SecretListResult{}, nil
43+
}
44+
returnSecrets := keyvault.NewSecretListResultPage(secretList, stopNext)
45+
client.EXPECT().GetSecret(gomock.Any(), a.vaultBaseURL, path, "").Return(keyvault.SecretBundle{Value: &shazam}, nil).AnyTimes()
46+
client.EXPECT().GetSecret(gomock.Any(), a.vaultBaseURL, "all", "").Return(keyvault.SecretBundle{Value: String("mailman")}, nil).AnyTimes()
47+
client.EXPECT().GetSecret(gomock.Any(), a.vaultBaseURL, "all-1", "").Return(keyvault.SecretBundle{Value: String("shazam")}, nil).AnyTimes()
48+
client.EXPECT().GetSecrets(gomock.Any(), a.vaultBaseURL, nil).Return(returnSecrets, nil).AnyTimes()
49+
50+
AssertProvider(t, &a, true)
51+
52+
}
53+
54+
func TestAzureKeyVaultFailures(t *testing.T) {
55+
ctrl := gomock.NewController(t)
56+
defer ctrl.Finish()
57+
client := mock_providers.NewMockAzureKeyVaultClient(ctrl)
58+
a := AzureKeyVault{
59+
client: client,
60+
logger: GetTestLogger(),
61+
vaultName: "test",
62+
vaultBaseURL: "https://test/",
63+
}
64+
65+
client.EXPECT().GetSecret(gomock.Any(), a.vaultBaseURL, "settings/{{stage}}/billing-svc", "").Return(keyvault.SecretBundle{}, errors.New("error")).AnyTimes()
66+
67+
_, err := a.Get(core.KeyPath{Env: "MG_KEY", Path: "settings/{{stage}}/billing-svc"})
68+
assert.NotNil(t, err)
69+
}

pkg/providers/mock_providers/azure_keyvault_mock.go

+96
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)