Skip to content

Commit 4cdd438

Browse files
authored
Merge cc86332 into b69be41
2 parents b69be41 + cc86332 commit 4cdd438

File tree

6 files changed

+340
-2
lines changed

6 files changed

+340
-2
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,19 @@ servers:
151151
client_id: client-id
152152
client_secret: client-secret
153153
api_path: KeyfactorAPI
154-
```
154+
```
155+
156+
## Configuration File Providers
157+
158+
Below are a list of configuration file providers that can be used to load configuration from a file if loading from disk
159+
is not desired.
160+
161+
### Azure Key Vault
162+
163+
To use Azure Key Vault as a configuration file provider, the code must either be running in an Azure environment or the
164+
environment configured with `az login`. The following environment variables will be used when they are not directly set.
165+
166+
| Name | Description | Default |
167+
|---------------------|---------------------------------------|---------|
168+
| AZURE_KEYVAULT_NAME | The name of the Azure KeyVault | |
169+
| AZURE_SECRET_NAME | The name of the Azure KeyVault secret | |

auth_providers/auth_core.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,8 @@ func (c *CommandAuthConfig) GetServerConfig() *Server {
731731
return &server
732732
}
733733

734+
type contextKey string
735+
734736
// Example usage of CommandAuthConfig
735737
//
736738
// This example demonstrates how to use CommandAuthConfig to authenticate to the Keyfactor Command API.

auth_providers/azure_keyvault.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package auth_providers
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
"time"
9+
10+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
11+
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
12+
)
13+
14+
const (
15+
EnvAzureVaultName = "AZURE_KEYVAULT_NAME"
16+
EnvAzureSecretName = "AZURE_SECRET_NAME"
17+
)
18+
19+
// ConfigProviderAzureKeyVault is an Authenticator that uses Azure Key Vault for authentication.
20+
type ConfigProviderAzureKeyVault struct {
21+
SecretName string `json:"secret_name"`
22+
VaultName string `json:"vault_name"`
23+
DefaultCredential *azidentity.DefaultAzureCredential
24+
CommandConfig *Config
25+
//TenantID string `json:"tenant_id;omitempty"`
26+
//SubscriptionID string `json:"subscription_id;omitempty"`
27+
//ResourceGroup string `json:"resource_group;omitempty"`
28+
}
29+
30+
// NewConfigProviderAzureKeyVault creates a new instance of ConfigProviderAzureKeyVault.
31+
func NewConfigProviderAzureKeyVault() *ConfigProviderAzureKeyVault {
32+
return &ConfigProviderAzureKeyVault{}
33+
}
34+
35+
// String returns a string representation of the ConfigProviderAzureKeyVault.
36+
func (a *ConfigProviderAzureKeyVault) String() string {
37+
return fmt.Sprintf("SecretName: %s, AzureVaultName: %s", a.SecretName, a.VaultName)
38+
}
39+
40+
// WithSecretName sets the secret name for authentication.
41+
func (a *ConfigProviderAzureKeyVault) WithSecretName(secretName string) *ConfigProviderAzureKeyVault {
42+
a.SecretName = secretName
43+
return a
44+
}
45+
46+
// WithVaultName sets the vault name for authentication.
47+
func (a *ConfigProviderAzureKeyVault) WithVaultName(vaultName string) *ConfigProviderAzureKeyVault {
48+
a.VaultName = vaultName
49+
return a
50+
}
51+
52+
// Authenticate authenticates to Azure.
53+
func (a *ConfigProviderAzureKeyVault) Authenticate() error {
54+
55+
vErr := a.Validate()
56+
if vErr != nil {
57+
return vErr
58+
}
59+
60+
// Create a context with a timeout
61+
ctx, cancel := context.WithTimeout(context.Background(), DefaultClientTimeout*time.Second)
62+
defer cancel()
63+
64+
// Add custom metadata to context
65+
ctx = context.WithValue(ctx, contextKey("operation"), "AzureAuthenticate")
66+
67+
// Try to authenticate using DefaultAzureCredential
68+
cred, err := azidentity.NewDefaultAzureCredential(nil)
69+
if err != nil {
70+
return fmt.Errorf("failed to obtain a credential: %w", err)
71+
}
72+
a.DefaultCredential = cred
73+
return nil
74+
}
75+
76+
// Validate validates the ConfigProviderAzureKeyVault.
77+
func (a *ConfigProviderAzureKeyVault) Validate() error {
78+
if a.SecretName == "" {
79+
// Check if the secret name is set in the environment
80+
if secretName := os.Getenv(EnvAzureSecretName); secretName != "" {
81+
a.SecretName = secretName
82+
} else {
83+
return fmt.Errorf("Azure KeyVault `SecretName` is required")
84+
}
85+
}
86+
if a.VaultName == "" {
87+
// Check if the vault name is set in the environment
88+
if vaultName := os.Getenv(EnvAzureVaultName); vaultName != "" {
89+
a.VaultName = vaultName
90+
} else {
91+
return fmt.Errorf("Azure KeyVault `VaultName` is required")
92+
}
93+
}
94+
return nil
95+
}
96+
97+
// LoadConfigFromAzureKeyVault loads a Config type from Azure Key Vault.
98+
func (a *ConfigProviderAzureKeyVault) LoadConfigFromAzureKeyVault() (*Config, error) {
99+
if a.DefaultCredential == nil {
100+
aErr := a.Authenticate()
101+
if aErr != nil {
102+
return nil, aErr
103+
}
104+
}
105+
106+
// Create a context with a timeout
107+
ctx, cancel := context.WithTimeout(context.Background(), DefaultClientTimeout*time.Second)
108+
defer cancel()
109+
110+
// Add custom metadata to context
111+
ctx = context.WithValue(ctx, contextKey("operation"), "LoadConfigFromAzureKeyVault")
112+
ctx = context.WithValue(ctx, contextKey("vaultName"), a.VaultName)
113+
ctx = context.WithValue(ctx, contextKey("secretName"), a.SecretName)
114+
// Create a client to access the Azure Key Vault
115+
vaultURL := fmt.Sprintf("https://%s.vault.azure.net/", a.VaultName)
116+
client, cErr := azsecrets.NewClient(vaultURL, a.DefaultCredential, nil)
117+
if cErr != nil {
118+
return nil, cErr
119+
}
120+
121+
// Retrieve the secret from the Azure Key Vault
122+
secretResp, sErr := client.GetSecret(ctx, a.SecretName, "", nil)
123+
if sErr != nil {
124+
return nil, sErr
125+
}
126+
127+
// Check if the secret value is nil to avoid dereferencing a nil pointer
128+
if secretResp.Value == nil {
129+
return nil, fmt.Errorf("secret value for '%s' in vault '%s' is nil", a.SecretName, a.VaultName)
130+
}
131+
132+
// Parse the secret value into a Config type
133+
var config Config
134+
if jErr := json.Unmarshal([]byte(*secretResp.Value), &config); jErr != nil {
135+
//attempt to unmarshal as a single server config
136+
var singleServerConfig Server
137+
if sjErr := json.Unmarshal([]byte(*secretResp.Value), &singleServerConfig); sjErr == nil {
138+
config.Servers = make(map[string]Server)
139+
config.Servers["default"] = singleServerConfig
140+
} else {
141+
return nil, jErr
142+
}
143+
}
144+
145+
a.CommandConfig = &config
146+
return &config, nil
147+
}
148+
149+
// Example usage of ConfigProviderAzureKeyVault
150+
//
151+
// This example demonstrates how to use ConfigProviderAzureKeyVault to load a configuration from Azure Key Vault.
152+
//
153+
// func ExampleConfigProviderAzureKeyVault() {
154+
// provider := NewConfigProviderAzureKeyVault().
155+
// WithSecretName("my-secret").
156+
// WithVaultName("my-vault")
157+
//
158+
// err := provider.Authenticate()
159+
// if err != nil {
160+
// fmt.Println("Failed to authenticate:", err)
161+
// return
162+
// }
163+
//
164+
// config, err := provider.LoadConfigFromAzureKeyVault()
165+
// if err != nil {
166+
// fmt.Println("Failed to load config from Azure Key Vault:", err)
167+
// return
168+
// }
169+
//
170+
// fmt.Println("Loaded config from Azure Key Vault:", config)
171+
// }
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package auth_providers_test
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/Keyfactor/keyfactor-auth-client-go/auth_providers"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestConfigProviderAzureKeyVault_Authenticate(t *testing.T) {
12+
provider := auth_providers.NewConfigProviderAzureKeyVault()
13+
err := provider.Authenticate()
14+
assert.NoError(t, err, "expected no error during authentication")
15+
}
16+
17+
func TestConfigProviderAzureKeyVault_LoadConfigFromAzureKeyVault(t *testing.T) {
18+
vaultName := os.Getenv(auth_providers.EnvAzureVaultName)
19+
secretName := os.Getenv(auth_providers.EnvAzureSecretName)
20+
21+
t.Logf("vaultName: %s, secretName: %s", vaultName, secretName)
22+
23+
// Test with environment variables set
24+
t.Logf("Testing with only environment variables set")
25+
envConf := auth_providers.NewConfigProviderAzureKeyVault()
26+
envConfig, cErr := envConf.LoadConfigFromAzureKeyVault()
27+
assert.NoError(t, cErr, "expected no error during config load")
28+
assert.NotNil(t, envConfig, "expected config to be loaded")
29+
30+
// Test with mixed environment variables and parameters set
31+
t.Logf("Testing with mixed environment variables and parameters set")
32+
os.Unsetenv(auth_providers.EnvAzureSecretName)
33+
envParamsSecretName := auth_providers.NewConfigProviderAzureKeyVault().
34+
WithSecretName(secretName)
35+
envParamsSecretNameConfig, envParamsSecretNameErr := envParamsSecretName.LoadConfigFromAzureKeyVault()
36+
assert.NoError(t, envParamsSecretNameErr, "expected no error during config load")
37+
assert.NotNil(t, envParamsSecretNameConfig, "expected config to be loaded")
38+
os.Setenv(auth_providers.EnvAzureSecretName, secretName)
39+
40+
// Test with mixed environment variables and parameters set
41+
t.Logf("Testing with mixed environment variables and parameters set")
42+
os.Unsetenv(auth_providers.EnvAzureVaultName)
43+
envParamsVaultName := auth_providers.NewConfigProviderAzureKeyVault().
44+
WithVaultName(vaultName)
45+
envParamsVaultNameConfig, envParamsVaultNameErr := envParamsVaultName.LoadConfigFromAzureKeyVault()
46+
assert.NoError(t, envParamsVaultNameErr, "expected no error during config load")
47+
assert.NotNil(t, envParamsVaultNameConfig, "expected config to be loaded")
48+
os.Setenv(auth_providers.EnvAzureVaultName, vaultName)
49+
50+
// Test with no environment variables set
51+
t.Logf("Testing with no environment variables set")
52+
unsetAkvEnvVars()
53+
fullParams := auth_providers.NewConfigProviderAzureKeyVault().
54+
WithSecretName(secretName).
55+
WithVaultName(vaultName)
56+
fullParamsConfig, fullParamsErr := fullParams.LoadConfigFromAzureKeyVault()
57+
assert.NoError(t, fullParamsErr, "expected no error during config load")
58+
assert.NotNil(t, fullParamsConfig, "expected config to be loaded")
59+
}
60+
61+
func TestConfigProviderAzureKeyVault_Validate(t *testing.T) {
62+
provider := auth_providers.NewConfigProviderAzureKeyVault().
63+
WithSecretName("my-secret").
64+
WithVaultName("my-vault")
65+
66+
err := provider.Validate()
67+
assert.NoError(t, err, "expected no error during validation")
68+
}
69+
70+
func unsetAkvEnvVars() {
71+
os.Unsetenv(auth_providers.EnvAzureSecretName)
72+
os.Unsetenv(auth_providers.EnvAzureVaultName)
73+
}

go.mod

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@ module github.com/Keyfactor/keyfactor-auth-client-go
1717
go 1.22
1818

1919
require (
20+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
21+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0
22+
github.com/stretchr/testify v1.9.0
2023
golang.org/x/oauth2 v0.24.0
2124
gopkg.in/yaml.v2 v2.4.0
2225
)
26+
27+
require (
28+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
29+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
30+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
31+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
32+
github.com/davecgh/go-spew v1.1.1 // indirect
33+
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
34+
github.com/google/uuid v1.6.0 // indirect
35+
github.com/kylelemons/godebug v1.1.0 // indirect
36+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
37+
github.com/pmezard/go-difflib v1.0.0 // indirect
38+
golang.org/x/crypto v0.27.0 // indirect
39+
golang.org/x/net v0.29.0 // indirect
40+
golang.org/x/sys v0.25.0 // indirect
41+
golang.org/x/text v0.18.0 // indirect
42+
gopkg.in/yaml.v3 v3.0.1 // indirect
43+
)

go.sum

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,64 @@
1+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
2+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
3+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
4+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw=
6+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ=
7+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
8+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
9+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0 h1:xnO4sFyG8UH2fElBkcqLTOZsAajvKfnSlgBBW8dXYjw=
10+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0/go.mod h1:XD3DIOOVgBCO03OleB1fHjgktVRFxlT++KwKgIOewdM=
11+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw=
12+
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
13+
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
14+
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
15+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
16+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
17+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
18+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
19+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
20+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
21+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
22+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
23+
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
24+
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
125
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
226
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
27+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
28+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
29+
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
30+
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
31+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
32+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
33+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
34+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
35+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
36+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
37+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
38+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
39+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
40+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
41+
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
42+
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
43+
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
44+
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
45+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
46+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
47+
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
48+
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
49+
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
50+
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
351
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
452
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
5-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
53+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54+
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
55+
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
56+
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
57+
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
658
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
59+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
60+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
761
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
862
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
63+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
64+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)