Skip to content

Commit

Permalink
[chore] Fix missing context, add timeout (#164)
Browse files Browse the repository at this point in the history
* fix missing context, add timeout

* bump version, update doc
  • Loading branch information
EladLeev authored Aug 26, 2024
1 parent 2ec2de9 commit abc6f5c
Show file tree
Hide file tree
Showing 24 changed files with 135 additions and 87 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Table of Contents
- [Prerequisites](#prerequisites)
- [Building Steps](#building-steps)
- [Examples](#examples)
- [Raw Mode](#raw-mode)
- [Contributing](#contributing)
- [License](#license)

Expand All @@ -52,6 +53,7 @@ The `SealedSecret` will be printed to `STDOUT`. You can run it as is, as part of
| `-l`, `--labels` | Sets k8s labels. KV pairs, comma separated. | | `[]string` |
| `--raw` | Use Kubeseal raw mode. | | `bool` |
| | | | |
| `-t`, `--timeout` | Set timeout to the secret fetch. Default: 30 | | `int` |
| `-d`, `--debug` | Run in debug mode. | | `bool` |
| `-h`, `--help` | Display help. | | `none` |
| `-v`, `--version` | Display version. | | `none` |
Expand Down Expand Up @@ -82,7 +84,7 @@ If not, `kubeseal-convert` will try to extract the project ID from the default c

* Go version 1.22+
* `make` command installed
* `kubeseal` command installed, and a valid communication to the sealed secrets controller.
* `kubeseal` command installed, and a valid communication to the Sealed Secrets controller.

### Building Steps

Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/azurekeyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var azureKeyVaultCmd = &cobra.Command{
PreRun: toggleDebug,
Run: func(cmd *cobra.Command, args []string) {
secretVal := domain.SecretValues{
Data: AzureKeyVault.GetSecrets(args[0]),
Data: AzureKeyVault.GetSecrets(args[0], timeout),
Name: ParseStringFlag(cmd, "name"),
Namespace: ParseStringFlag(cmd, "namespace"),
Labels: ParseLabels(cmd),
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/azurekeyvault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestAzureKeyVaultCmd(t *testing.T) {

// mock azurekeyvault
mockAzureKeyVault := mocks.NewAzureKeyVault(t)
mockAzureKeyVault.On("GetSecrets", mock.Anything).Return(map[string]interface{}{
mockAzureKeyVault.On("GetSecrets", mock.Anything, mock.AnythingOfType("int")).Return(map[string]interface{}{
"key": "value",
}, nil)
AzureKeyVault = mockAzureKeyVault
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/gcpsecretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var gcpSecretsmanagerCmd = &cobra.Command{
PreRun: toggleDebug,
Run: func(cmd *cobra.Command, args []string) {
secretVal := domain.SecretValues{
Data: GcpSecretsManager.GetSecret(args[0]),
Data: GcpSecretsManager.GetSecret(args[0], timeout),
Name: ParseStringFlag(cmd, "name"),
Namespace: ParseStringFlag(cmd, "namespace"),
Labels: ParseLabels(cmd),
Expand Down
4 changes: 3 additions & 1 deletion cmd/kubeseal-convert/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ var (
secretName string
secretNamespace string
rawMode bool
timeout int

version = "3.2.0"
version = "3.3.0"
rootCmd = &cobra.Command{
Use: "kubeseal-convert",
Short: "kubeseal-convert - a simple CLI to transform external secrets into Sealed Secrets",
Expand Down Expand Up @@ -54,4 +55,5 @@ func init() {
rootCmd.PersistentFlags().StringToStringP("labels", "l", map[string]string{}, "Set k8s labels")
rootCmd.PersistentFlags().BoolVar(&rawMode, "raw", false, "[optional] use raw mode")
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "[optional] debug logging")
rootCmd.PersistentFlags().IntVarP(&timeout, "timeout", "t", 30, "[optional] get secret timeout")
}
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/secretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var secretsmanagerCmd = &cobra.Command{
PreRun: toggleDebug,
Run: func(cmd *cobra.Command, args []string) {
secretVal := domain.SecretValues{
Data: SecretsManager.GetSecret(args[0]),
Data: SecretsManager.GetSecret(args[0], timeout),
Name: ParseStringFlag(cmd, "name"),
Namespace: ParseStringFlag(cmd, "namespace"),
Labels: ParseLabels(cmd),
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/secretsmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestSecretManagerCmd(t *testing.T) {
// mock secretsmanager
mockSecretsManager := mocks.NewSecretsManager(t)

mockSecretsManager.On("GetSecret", mock.Anything).Return(map[string]interface{}{"key": "value"}, nil)
mockSecretsManager.On("GetSecret", mock.Anything, mock.AnythingOfType("int")).Return(map[string]interface{}{"key": "value"}, nil)
SecretsManager = mockSecretsManager

// test sm command
Expand Down
4 changes: 2 additions & 2 deletions cmd/kubeseal-convert/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ var vaultCmd = &cobra.Command{
PreRun: toggleDebug,
Run: func(cmd *cobra.Command, args []string) {
secretVal := domain.SecretValues{
Data: Vault.GetSecret(args[0]),
Data: Vault.GetSecret(args[0], timeout),
Name: ParseStringFlag(cmd, "name"),
Namespace: ParseStringFlag(cmd, "namespace"),
Labels: ParseLabels(cmd),
Annotations: ParseAnnotations(cmd),
}
log.Debugf("secret values: %v", secretVal)
KubeSeal.BuildSecretFile(secretVal, false)
KubeSeal.BuildSecretFile(secretVal, rawMode)
},
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeseal-convert/vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestVaultCmd(t *testing.T) {

// mock vault
mockVault := mocks.NewVault(t)
mockVault.On("GetSecret", mock.Anything).Return(map[string]interface{}{
mockVault.On("GetSecret", mock.Anything, mock.AnythingOfType("int")).Return(map[string]interface{}{
"key": "value",
}, nil)
Vault = mockVault
Expand Down
10 changes: 5 additions & 5 deletions mocks/AzureKeyVault.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions mocks/GcpSecretsManager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions mocks/SecretsManager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions mocks/Vault.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 30 additions & 15 deletions pkg/kubeseal-convert/handlers/azurekeyvault/azurekeyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package azurekeyvault
import (
"context"
"fmt"
"time"

log "github.com/sirupsen/logrus"

Expand All @@ -12,47 +13,49 @@ import (
"github.com/eladleev/kubeseal-convert/pkg/kubeseal-convert/interfaces"
)

func createClient(vaultName string) *secrets.Client {
// see https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-defaultazurecredential: this allows getting credentials
// via either environment variables, managed identity, or 'az login'
func createClient(vaultName string) (*secrets.Client, error) {
/*
see https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-defaultazurecredential: this allows getting credentials
via either environment variables, managed identity, or 'az login'
*/
cred, err := identity.NewDefaultAzureCredential(nil)
log.Debugf("Azure identity: %v", cred)
if err != nil {
log.Fatalf("Failed to obtain a credential needed to login to the azure vault: %v", err)
return nil, fmt.Errorf("failed to obtain a credential needed to login to the azure vault: %v", err)
}

vaultURI := fmt.Sprintf("https://%s.vault.azure.net", vaultName)
log.Debugf("vaultURI: %v", vaultURI)
client, err := secrets.NewClient(vaultURI, cred, nil)
if err != nil {
log.Fatalf("Failed to connect to vault '%s': %v", vaultURI, err)
return nil, fmt.Errorf("failed to connect to vault '%s': %v", vaultURI, err)
}
return client
return client, nil
}

// retrieve secret by name with the client
func getSecrets(client *secrets.Client, vaultName string) map[string]interface{} {
func getSecrets(ctx context.Context, client *secrets.Client, vaultName string) (map[string]interface{}, error) {
mp := make(map[string]interface{})

pager := client.NewListSecretsPager(&secrets.ListSecretsOptions{})

for pager.More() {
log.Debugf("pager: %v", pager)
page, err := pager.NextPage(context.TODO())
page, err := pager.NextPage(ctx)
if err != nil {
log.Fatalf("Failed to retrieve secrets from vault '%s': %v", vaultName, err)
return nil, fmt.Errorf("failed to retrieve secrets from vault '%s': %v", vaultName, err)
}
for _, secret := range page.Value {
value, err := client.GetSecret(context.TODO(), secret.ID.Name(), secret.ID.Version(), &secrets.GetSecretOptions{})
value, err := client.GetSecret(ctx, secret.ID.Name(), secret.ID.Version(), &secrets.GetSecretOptions{})
log.Debugf("secret value: %v", value)
if err != nil {
log.Fatalf("Failed to retrieve secret '%s' from vault '%s': %v", secret.ID.Name(), vaultName, err)
return nil, fmt.Errorf("failed to retrieve secret '%s' from vault '%s': %v", secret.ID.Name(), vaultName, err)
}
mp[secret.ID.Name()] = *value.Value
}
}

return mp
return mp, nil
}

type AzureKeyVaultImp struct {
Expand All @@ -62,8 +65,20 @@ func New() interfaces.AzureKeyVault {
return &AzureKeyVaultImp{}
}

func (*AzureKeyVaultImp) GetSecrets(vaultName string) map[string]interface{} {
func (*AzureKeyVaultImp) GetSecrets(vaultName string, timeout int) map[string]interface{} {
log.Debugf("Getting secrets from vault %v", vaultName)
cli := createClient(vaultName)
return getSecrets(cli, vaultName)
// Create a context with a timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()

cli, err := createClient(vaultName)
if err != nil {
log.Fatalf("failed to create client: %v", err)
}

secret, err := getSecrets(ctx, cli, vaultName)
if err != nil {
log.Fatalf("failed to get secret: %v", err)
}
return secret
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"time"

log "github.com/sirupsen/logrus"

Expand Down Expand Up @@ -37,14 +38,13 @@ func buildSecretId(ctx context.Context, secretName string) string {
return secretName
}

func getSecret(secretName string) map[string]interface{} {
func getSecret(ctx context.Context, secretName string) (map[string]interface{}, error) {
mp := make(map[string]interface{})
ctx := context.Background()

client, err := secretmanager.NewClient(ctx)
log.Debugf("client: %v", client)
if err != nil {
log.Fatalf("failed to setup client: %v", err)
return nil, fmt.Errorf("failed to setup client: %v", err)
}

defer func(client *secretmanager.Client) {
Expand All @@ -59,11 +59,11 @@ func getSecret(secretName string) map[string]interface{} {
result, err := client.AccessSecretVersion(ctx, accessRequest)
log.Debugf("result: %v", result)
if err != nil {
log.Fatalf("failed to access secret version: %v", err)
return nil, fmt.Errorf("failed to access secret version: %v", err)
}

mp[cleanSecretName] = string(result.Payload.Data)
return mp
return mp, nil
}

type GcpSecretsManagerImp struct {
Expand All @@ -73,6 +73,13 @@ func New() interfaces.SecretsManager {
return &GcpSecretsManagerImp{}
}

func (*GcpSecretsManagerImp) GetSecret(secretName string) map[string]interface{} {
return getSecret(secretName)
func (*GcpSecretsManagerImp) GetSecret(secretName string, timeout int) map[string]interface{} {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()

secret, err := getSecret(ctx, secretName)
if err != nil {
log.Errorf("failed to get secret: %v", err)
}
return secret
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func Test_getSecret(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getSecret(tt.args.secretName); !reflect.DeepEqual(got, tt.want) {
if got, _ := getSecret(context.TODO(), tt.args.secretName); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getSecret() = %v, want %v", got, tt.want)
}
})
Expand Down
Loading

0 comments on commit abc6f5c

Please sign in to comment.