Skip to content

Instantly share code, notes, and snippets.

@jackstine
Created February 11, 2025 16:46
Show Gist options
  • Select an option

  • Save jackstine/32d47cf5b6b3fa4fe136511e94cbf60f to your computer and use it in GitHub Desktop.

Select an option

Save jackstine/32d47cf5b6b3fa4fe136511e94cbf60f to your computer and use it in GitHub Desktop.
output from GPT
Got it! You need a structured way to retrieve values using a combination of configuration, environment variables, and AWS Secrets Manager. Let’s break this down into logical Go components:
• Configuration Loader: Reads the configuration file to define how values are retrieved.
• Static Value Getter: Acts as a manager that determines if a value comes from an environment variable or AWS Secrets Manager.
• Secret Retriever: Handles fetching and parsing secrets from AWS Secrets Manager.
• Structs: Holds the values retrieved for easy access.
Project Structure
valuegetter/
│── config/
│ ├── config.go # Loads configuration file
│── secrets/
│ ├── aws_secrets.go # Handles AWS Secrets Manager retrieval
│── values/
│ ├── getter.go # StaticValueGetter logic
│── main.go # Example usage
1. Configuration Loader (config/config.go)
This will load a configuration file (e.g., JSON, YAML) defining how values should be retrieved.
package config
import (
"encoding/json"
"os"
)
// Config represents the structure for value retrieval configuration.
type Config struct {
Values map[string]ValueConfig `json:"values"`
}
// ValueConfig defines how a value should be retrieved.
type ValueConfig struct {
EnvOverride string `json:"env_override"` // Environment variable name
AwsSecret string `json:"aws_secret"` // AWS Secret key
JsonKey string `json:"json_key"` // JSON key inside AWS Secret
}
// LoadConfig reads configuration from a JSON file.
func LoadConfig(filePath string) (*Config, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
var cfg Config
err = json.Unmarshal(data, &cfg)
if err != nil {
return nil, err
}
return &cfg, nil
}
2. AWS Secrets Manager Retriever (secrets/aws_secrets.go)
This handles AWS Secrets retrieval and JSON parsing.
package secrets
import (
"encoding/json"
"errors"
"os"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/secretsmanager"
)
// GetSecret retrieves a secret from AWS Secrets Manager.
func GetSecret(secretName string) (map[string]string, error) {
sess := session.Must(session.NewSession())
svc := secretsmanager.New(sess)
input := &secretsmanager.GetSecretValueInput{
SecretId: &secretName,
}
result, err := svc.GetSecretValue(input)
if err != nil {
return nil, err
}
var secretMap map[string]string
if err := json.Unmarshal([]byte(*result.SecretString), &secretMap); err != nil {
return nil, err
}
return secretMap, nil
}
// GetSecretValue retrieves a specific value from AWS Secrets Manager.
func GetSecretValue(secretName, key string) (string, error) {
secrets, err := GetSecret(secretName)
if err != nil {
return "", err
}
value, exists := secrets[key]
if !exists {
return "", errors.New("key not found in secret")
}
return value, nil
}
3. Static Value Getter (values/getter.go)
This checks for environment overrides first, then falls back to AWS Secrets Manager.
package values
import (
"fmt"
"os"
"sync"
"github.com/your_project/config"
"github.com/your_project/secrets"
)
// ValueGetter manages cached values.
type ValueGetter struct {
mu sync.RWMutex
values map[string]string
config *config.Config
}
// NewValueGetter initializes a ValueGetter with configuration.
func NewValueGetter(cfg *config.Config) *ValueGetter {
return &ValueGetter{
values: make(map[string]string),
config: cfg,
}
}
// GetSomeValue retrieves a value based on the configuration.
func (vg *ValueGetter) GetSomeValue(key string) (string, error) {
vg.mu.RLock()
if val, exists := vg.values[key]; exists {
vg.mu.RUnlock()
return val, nil
}
vg.mu.RUnlock()
cfg, exists := vg.config.Values[key]
if !exists {
return "", fmt.Errorf("no configuration found for key: %s", key)
}
// Check for environment override
if envValue, found := os.LookupEnv(cfg.EnvOverride); found {
vg.setValue(key, envValue)
return envValue, nil
}
// Fallback to AWS Secrets Manager
secretValue, err := secrets.GetSecretValue(cfg.AwsSecret, cfg.JsonKey)
if err != nil {
return "", err
}
vg.setValue(key, secretValue)
return secretValue, nil
}
// setValue caches the retrieved value.
func (vg *ValueGetter) setValue(key, value string) {
vg.mu.Lock()
defer vg.mu.Unlock()
vg.values[key] = value
}
4. Example Usage (main.go)
This demonstrates how to use the ValueGetter.
package main
import (
"fmt"
"log"
"github.com/your_project/config"
"github.com/your_project/values"
)
func main() {
// Load config
cfg, err := config.LoadConfig("config.json")
if err != nil {
log.Fatalf("Error loading config: %v", err)
}
// Create ValueGetter
valueGetter := values.NewValueGetter(cfg)
// Get a value
key := "database_password"
value, err := valueGetter.GetSomeValue(key)
if err != nil {
log.Fatalf("Error getting value: %v", err)
}
fmt.Printf("Retrieved value for %s: %s\n", key, value)
}
5. Example Config (config.json)
This defines how values should be retrieved.
{
"values": {
"database_password": {
"env_override": "DB_PASSWORD",
"aws_secret": "MyDatabaseSecrets",
"json_key": "password"
},
"api_key": {
"env_override": "API_KEY",
"aws_secret": "ApiSecrets",
"json_key": "key"
}
}
}
How This Works
1. Check Environment Variable
• If DB_PASSWORD exists, use it.
2. Otherwise, Retrieve from AWS Secrets Manager
• Use MyDatabaseSecrets and get the password key.
3. Cache the Value
• Avoid repeated AWS requests.
Why This Approach?
✅ Thread-safe caching (via sync.RWMutex)
✅ Environment-first approach (allows local overrides)
✅ AWS Secrets Manager integration (secure secret retrieval)
✅ Flexible JSON configuration (easy modifications)
Would you like any refinements? 🚀
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment