Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add API caching for list secrets #28

Merged
merged 4 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/go-resty/resty/v2"
"github.com/hashicorp/golang-lru/v2/expirable"
api "github.com/infisical/go-sdk/packages/api/auth"
"github.com/infisical/go-sdk/packages/models"
"github.com/infisical/go-sdk/packages/util"
Expand All @@ -26,6 +27,8 @@ type InfisicalClient struct {

mu sync.RWMutex

cache *expirable.LRU[string, interface{}]

httpClient *resty.Client
config Config

Expand All @@ -48,11 +51,12 @@ type InfisicalClientInterface interface {
}

type Config struct {
SiteUrl string `default:"https://app.infisical.com"`
CaCertificate string
UserAgent string `default:"infisical-go-sdk"` // User-Agent header to be used on requests sent by the SDK. Defaults to `infisical-go-sdk`. Do not modify this unless you have a reason to do so.
AutoTokenRefresh bool `default:"true"` // Wether or not to automatically refresh the auth token after using one of the .Auth() methods. Defaults to `true`.
SilentMode bool `default:"false"` // If enabled, the SDK will not print any warnings to the console.
SiteUrl string `default:"https://app.infisical.com"`
CaCertificate string
UserAgent string `default:"infisical-go-sdk"` // User-Agent header to be used on requests sent by the SDK. Defaults to `infisical-go-sdk`. Do not modify this unless you have a reason to do so.
AutoTokenRefresh bool `default:"true"` // Wether or not to automatically refresh the auth token after using one of the .Auth() methods. Defaults to `true`.
SilentMode bool `default:"false"` // If enabled, the SDK will not print any warnings to the console.
CacheExpiryInSeconds int // Defines how long certain API responses should be cached in memory, in seconds. When set to a positive value, responses from specific fetch API requests (like secret fetching) will be cached for this duration. Set to 0 to disable caching. Defaults to 0.
}

func setDefaults(cfg *Config) {
Expand Down Expand Up @@ -123,6 +127,10 @@ func NewInfisicalClient(context context.Context, config Config) InfisicalClientI
client.dynamicSecrets = NewDynamicSecrets(client)
client.kms = NewKms(client)
client.ssh = NewSsh(client)
if config.CacheExpiryInSeconds != 0 {
// hard limit set at 1000 cache items until forced eviction
client.cache = expirable.NewLRU[string, interface{}](1000, nil, time.Second*time.Duration(config.CacheExpiryInSeconds))
}

if config.AutoTokenRefresh {
go client.handleTokenLifeCycle(context)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/aws/aws-sdk-go-v2 v1.27.2
github.com/aws/aws-sdk-go-v2/config v1.27.18
github.com/go-resty/resty/v2 v2.13.1
github.com/hashicorp/golang-lru/v2 v2.0.7
google.golang.org/api v0.188.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
Expand Down
25 changes: 23 additions & 2 deletions packages/api/secrets/list_secrets.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
package api

import (
"encoding/json"
"fmt"

"github.com/go-resty/resty/v2"
"github.com/hashicorp/golang-lru/v2/expirable"
"github.com/infisical/go-sdk/packages/errors"
"github.com/infisical/go-sdk/packages/util"
)

const callListSecretsV3RawOperation = "CallListSecretsV3Raw"

func CallListSecretsV3(httpClient *resty.Client, request ListSecretsV3RawRequest) (ListSecretsV3RawResponse, error) {

func CallListSecretsV3(cache *expirable.LRU[string, interface{}], httpClient *resty.Client, request ListSecretsV3RawRequest) (ListSecretsV3RawResponse, error) {
var cacheKey string

if cache != nil {
reqBytes, err := json.Marshal(request)
if err != nil {
return ListSecretsV3RawResponse{}, err
}
cacheKey = util.ComputeCacheKeyFromBytes(reqBytes)
if cached, found := cache.Get(cacheKey); found {
if response, ok := cached.(ListSecretsV3RawResponse); ok {
return response, nil
}
cache.Remove(cacheKey)
}
}
secretsResponse := ListSecretsV3RawResponse{}

if request.SecretPath == "" {
Expand All @@ -37,5 +54,9 @@ func CallListSecretsV3(httpClient *resty.Client, request ListSecretsV3RawRequest
return ListSecretsV3RawResponse{}, errors.NewAPIErrorWithResponse(callListSecretsV3RawOperation, res)
}

if cache != nil {
cache.Add(cacheKey, secretsResponse)
}

return secretsResponse, nil
}
24 changes: 15 additions & 9 deletions packages/models/secrets.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package models

type SecretMetadata struct {
Key string `json:"key"`
Value string `json:"value"`
}

type Secret struct {
ID string `json:"id"`
Workspace string `json:"workspace"`
Environment string `json:"environment"`
Version int `json:"version"`
Type string `json:"type"`
SecretKey string `json:"secretKey"`
SecretValue string `json:"secretValue"`
SecretComment string `json:"secretComment"`
SecretPath string `json:"secretPath,omitempty"`
ID string `json:"id"`
Workspace string `json:"workspace"`
Environment string `json:"environment"`
Version int `json:"version"`
Type string `json:"type"`
SecretKey string `json:"secretKey"`
SecretValue string `json:"secretValue"`
SecretComment string `json:"secretComment"`
SecretPath string `json:"secretPath,omitempty"`
SecretMetadata []SecretMetadata `json:"secretMetadata"`
}

type SecretImport struct {
Expand Down
7 changes: 7 additions & 0 deletions packages/util/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package util

import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -103,3 +105,8 @@ func SleepWithContext(ctx context.Context, duration time.Duration) error {
return nil
}
}

func ComputeCacheKeyFromBytes(bytes []byte) string {
key := sha256.Sum256(bytes)
return hex.EncodeToString(key[:])
}
2 changes: 1 addition & 1 deletion secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Secrets struct {
}

func (s *Secrets) List(options ListSecretsOptions) ([]models.Secret, error) {
res, err := api.CallListSecretsV3(s.client.httpClient, options)
res, err := api.CallListSecretsV3(s.client.cache, s.client.httpClient, options)

if err != nil {
return nil, err
Expand Down
Loading