Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4e6c902
feat: FQ refactorings with upstream changes
KaivalyaMDabhadkar Oct 24, 2025
e9f54bd
fix: fixed merge conflicts
KaivalyaMDabhadkar Oct 26, 2025
d9c7b80
fix: addressed some reviews
KaivalyaMDabhadkar Oct 26, 2025
959b7c6
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 26, 2025
dca1f92
fix: fixed merge conflicts
KaivalyaMDabhadkar Oct 27, 2025
bdc9dd5
fix: addressed reviews
KaivalyaMDabhadkar Oct 27, 2025
8b60fa6
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 27, 2025
1db4844
fix: addressed reviews
KaivalyaMDabhadkar Oct 28, 2025
5f75cab
fix: merged conflicts and addressed reviews
KaivalyaMDabhadkar Oct 28, 2025
3294419
fix: addressed reviews
KaivalyaMDabhadkar Oct 28, 2025
e287bb2
fix: addressed reviews
KaivalyaMDabhadkar Oct 28, 2025
d59107f
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 28, 2025
90e021d
fix: removed redundant code comments
KaivalyaMDabhadkar Oct 28, 2025
c547932
fix: added better error handling in reconciler
KaivalyaMDabhadkar Oct 28, 2025
2803e81
fix: resolved conflicts
KaivalyaMDabhadkar Oct 28, 2025
619ab21
Merge branch 'main' into kdabhadkar/FQ-refactorings
mchmarny Oct 28, 2025
763e69d
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 29, 2025
611cf45
fix: addressed review comments
KaivalyaMDabhadkar Oct 29, 2025
4e29ff6
Merge branch 'kdabhadkar/FQ-refactorings' of github.com:KaivalyaMDabh…
KaivalyaMDabhadkar Oct 29, 2025
2a925f7
fix: fixed go.mod in different components
KaivalyaMDabhadkar Oct 29, 2025
abe7105
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 29, 2025
b2ca766
fix: addressed review comment
KaivalyaMDabhadkar Oct 29, 2025
d392907
Merge remote-tracking branch 'upstream/main' into kdabhadkar/FQ-refac…
KaivalyaMDabhadkar Oct 29, 2025
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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ updates:
interval: "weekly"
labels: ["dependencies"]

- package-ecosystem: "gomod"
directory: "/commons"
target-branch: "main"
schedule:
interval: "weekly"
labels: ["dependencies"]

# Fault Management
- package-ecosystem: "gomod"
directory: "/fault-quarantine-module"
Expand Down
10 changes: 5 additions & 5 deletions commons/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Logger SDK Makefile
# Commons Makefile
# Individual module build and test targets

# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
Expand All @@ -19,7 +19,7 @@
# MODULE-SPECIFIC CONFIGURATION
# =============================================================================

# Logger-sdk specific settings (library module - no Docker or binary)
# Commons specific settings (library module - no Docker or binary)
IS_GO_MODULE := 1
HAS_DOCKER := 0

Expand All @@ -40,8 +40,8 @@ all: lint-test ## Run lint-test (default target)
# =============================================================================
# MODULE NOTES
# =============================================================================
# This is a library module providing centralized logging initialization.
# - Library module providing structured logging utilities
# This is a library module providing common utilities for NVSentinel.
# - Library module providing structured logging and configuration management
# - No binary output or Docker builds
# - Used by all nvsentinel modules for consistent logging setup
# - Used by all nvsentinel modules for consistent logging and config loading
# Run 'make help' to see available targets
3 changes: 2 additions & 1 deletion commons/go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/nvidia/nvsentinel/logger-sdk
module github.com/nvidia/nvsentinel/commons

go 1.24.0

toolchain go1.24.8

require (
github.com/BurntSushi/toml v1.5.0
github.com/prometheus/client_golang v1.23.2
golang.org/x/sync v0.17.0
)
Expand Down
2 changes: 2 additions & 0 deletions commons/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down
229 changes: 229 additions & 0 deletions commons/pkg/configmanager/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package configmanager

import (
"fmt"
"os"
"strconv"
"strings"
)

// GetEnvVar retrieves an environment variable and converts it to type T.
// Type must be explicitly specified: GetEnvVar[int]("PORT")
// If no default value is provided, the environment variable is required.
// If a default value is provided, it will be used when the environment variable is not set.
// Optional validator function can be provided - it should return nil if validation passes, error otherwise.
//
// Supported types:
// - int
// - uint
// - float64
// - bool (accepts: "true" or "false", case-insensitive)
// - string
//
// Example usage:
//
// // Required env var (must specify type explicitly)
// port, err := configmanager.GetEnvVar[int]("PORT")
//
// // With default value (type can be inferred)
// timeout, err := configmanager.GetEnvVar[int]("TIMEOUT", 30)
//
// // With default and validation
// maxConn, err := configmanager.GetEnvVar[int]("MAX_CONN", 100, func(v int) error {
// if v <= 0 { return fmt.Errorf("must be positive") }
// return nil
// })
//
// // Required with validation (must specify type explicitly)
// workers, err := configmanager.GetEnvVar[int]("WORKERS", func(v int) error {
// if v <= 0 { return fmt.Errorf("must be positive") }
// return nil
// })
func GetEnvVar[T any](name string, defaultAndValidator ...any) (T, error) {
var zero T

var defaultValue *T

var validator func(T) error

// Parse variadic arguments: [defaultValue], [validator], or [defaultValue, validator]
for _, arg := range defaultAndValidator {
switch v := arg.(type) {
case func(T) error:
validator = v
case T:
defaultValue = &v
}
}

valueStr, exists := os.LookupEnv(name)
if !exists {
// If env var not set, use default if provided
if defaultValue != nil {
return *defaultValue, nil
}

return zero, fmt.Errorf("environment variable %s is not set", name)
}

// Convert string to type T
value, err := parseValue[T](valueStr)
if err != nil {
return zero, fmt.Errorf("error converting %s: %w", name, err)
}

// Validate if validator is provided
if validator != nil {
if err := validator(value); err != nil {
return zero, fmt.Errorf("validation failed for %s: %w", name, err)
}
}

return value, nil
}

func parseValue[T any](valueStr string) (T, error) {
var zero T

switch any(zero).(type) {
case string:
return any(valueStr).(T), nil
case int:
return parseAndConvert[T](parseInt(valueStr))
case uint:
return parseAndConvert[T](parseUint(valueStr))
case float64:
return parseAndConvert[T](parseFloat64(valueStr))
case bool:
return parseAndConvert[T](parseBool(valueStr))
default:
return zero, fmt.Errorf("unsupported type %T", zero)
}
}

func parseAndConvert[T any](value any, err error) (T, error) {
var zero T
if err != nil {
return zero, err
}

return any(value).(T), nil
}

func parseInt(valueStr string) (int, error) {
return strconv.Atoi(valueStr)
}

func parseUint(valueStr string) (uint, error) {
v, err := strconv.ParseUint(valueStr, 10, 0)
if err != nil {
return 0, err
}

return uint(v), nil
}

func parseFloat64(valueStr string) (float64, error) {
return strconv.ParseFloat(valueStr, 64)
}

// parseBool parses boolean values (accepts "true" or "false")
func parseBool(valueStr string) (bool, error) {
valueStr = strings.ToLower(strings.TrimSpace(valueStr))

switch valueStr {
case "true":
return true, nil
case "false":
return false, nil
default:
return false, fmt.Errorf("invalid boolean value: %s (must be 'true' or 'false')", valueStr)
}
}

// EnvVarSpec defines a specification for reading an environment variable.
// All fields except Name are optional.
//
// Example usage:
//
// specs := []configmanager.EnvVarSpec{
// {Name: "DATABASE_URL"}, // Required by default
// {Name: "PORT", Optional: true, DefaultValue: "5432"}, // Optional with default
// }
// envVars, errors := configmanager.ReadEnvVars(specs)
// if len(errors) > 0 {
// return fmt.Errorf("missing required vars: %v", errors)
// }
type EnvVarSpec struct {
Name string // Required: The environment variable name to read
Optional bool // Optional: If true, env var is optional; if false, it's required (default: false/required)
DefaultValue string // Optional: Value to use when env var is not set (default: "")
}

// ReadEnvVars reads multiple environment variables based on the provided specifications.
// Returns a map of environment variable names to their values and a slice of errors.
// Environment variables are required by default unless Optional is set to true.
//
// Example usage:
//
// specs := []configmanager.EnvVarSpec{
// {Name: "MONGODB_URI"},
// {Name: "MONGODB_DATABASE_NAME"},
// {Name: "MONGODB_PORT", Optional: true, DefaultValue: "27017"},
// }
// envVars, errors := configmanager.ReadEnvVars(specs)
// if len(errors) > 0 {
// log.Fatalf("Missing required environment variables: %v", errors)
// }
// // Use the values
// dbURI := envVars["MONGODB_URI"]
// dbName := envVars["MONGODB_DATABASE_NAME"]
// dbPort := envVars["MONGODB_PORT"] // Will be "27017" if not set
func ReadEnvVars(specs []EnvVarSpec) (map[string]string, []error) {
results := make(map[string]string)

var errors []error

for _, spec := range specs {
value, exists := os.LookupEnv(spec.Name)

if !exists {
value, err := handleMissingEnvVar(spec)
if err != nil {
errors = append(errors, err)
}

if value != "" || spec.Optional {
results[spec.Name] = value
}

continue
}

results[spec.Name] = value
}

return results, errors
}

func handleMissingEnvVar(spec EnvVarSpec) (string, error) {
if spec.Optional {
return spec.DefaultValue, nil
}

return "", fmt.Errorf("required environment variable %s is not set", spec.Name)
}
Loading