Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 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 @@ -59,6 +59,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
8 changes: 4 additions & 4 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 Down Expand Up @@ -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
1 change: 1 addition & 0 deletions commons/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.25
toolchain go1.25.3

require (
github.com/BurntSushi/toml v1.5.0
github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
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
244 changes: 244 additions & 0 deletions commons/pkg/configmanager/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// 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"
"math"
"os"
"strconv"
"strings"
)

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

valueStr, exists := os.LookupEnv(name)
if !exists {
return handleMissingEnvVarWithDefault(name, defaultValue, validator)
}

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

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

return value, nil
}

func handleMissingEnvVarWithDefault[T any](name string, defaultValue *T, validator func(T) error) (T, error) {
var zero T

if defaultValue == nil {
return zero, fmt.Errorf("environment variable %s is not set", name)
}

if validator != nil {
if err := validator(*defaultValue); err != nil {
return zero, fmt.Errorf("validation failed for default value of %s: %w", name, err)
}
}

return *defaultValue, 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) {
v, err := strconv.ParseInt(valueStr, 10, 64)
if err != nil {
return 0, err
}

if v < math.MinInt || v > math.MaxInt {
return 0, fmt.Errorf("value %d out of range for int type", v)
}

return int(v), nil
}

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

if v > math.MaxUint {
return 0, fmt.Errorf("value %d out of range for uint type", v)
}

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 is used when env var is not set.
// Empty string defaults are treated as "no value" and excluded from results map.
DefaultValue string
}

// 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"}, // Required
// {Name: "MONGODB_DATABASE_NAME"}, // Required
// {Name: "MONGODB_PORT", Optional: true, DefaultValue: "27017"}, // Included with default
// {Name: "DEBUG_MODE", Optional: true, DefaultValue: ""}, // NOT included (empty default)
// }
// 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 {
defaultVal, err := handleMissingEnvVar(spec)
if err != nil {
errors = append(errors, err)
}

// Only include non-empty defaults in results map to distinguish "not set" from "set to empty"
if defaultVal != "" {
results[spec.Name] = defaultVal
}

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
Loading