diff --git a/internal/adaptors/dbconfig/dbconfig.go b/internal/adaptors/dbconfig/dbconfig.go new file mode 100644 index 000000000..e51107e23 --- /dev/null +++ b/internal/adaptors/dbconfig/dbconfig.go @@ -0,0 +1,164 @@ +package dbconfig + +import ( + "fmt" + "strconv" +) + +const ( + DefaultNamespace = "" + GlobalConfigFile = "/var/run/redis/sonic-db/database_global.json" + ConfigFile = "/var/run/redis/sonic-db/database_config.json" +) + +type provider interface { + initialize() error + reset() error + namespaces() ([]string, error) + dbList(namespace string) ([]string, error) + dbID(name, namespace string) (int, error) + dbSeparator(name, namespace string) (string, error) + dbSocket(name, namespace string) (string, error) + dbHostname(name, namespace string) (string, error) + dbPort(name, namespace string) (int, error) +} + +var initialized bool + +func Init() (err error) { + defer catchException(&err) + initialized = false + return activeProvider.reset() +} + +func DbInit() (err error) { + defer catchException(&err) + if initialized { + return nil + } + if err := activeProvider.initialize(); err != nil { + return err + } + initialized = true + return nil +} + +func GetDbDefaultNamespace() (string, error) { + return DefaultNamespace, nil +} + +func CheckDbMultiNamespace() (multi bool, err error) { + defer catchException(&err) + namespaces, err := GetDbAllNamespaces() + if err != nil { + return false, err + } + return len(namespaces) > 1, nil +} + +func GetDbNonDefaultNamespaces() (nonDefault []string, err error) { + defer catchException(&err) + namespaces, err := GetDbAllNamespaces() + if err != nil { + return nil, err + } + nonDefault = make([]string, 0, len(namespaces)) + for _, namespace := range namespaces { + if namespace != DefaultNamespace { + nonDefault = append(nonDefault, namespace) + } + } + return nonDefault, nil +} + +func GetDbAllNamespaces() (namespaces []string, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return nil, err + } + return activeProvider.namespaces() +} + +func GetDbNamespaceFromTarget(target string) (namespace string, found bool, err error) { + defer catchException(&err) + if target == DefaultNamespace { + return target, true, nil + } + namespaces, err := GetDbAllNamespaces() + if err != nil { + return "", false, err + } + for _, namespace := range namespaces { + if target == namespace { + return target, true, nil + } + } + return "", false, nil +} + +func GetDbList(namespace string) (databases []string, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return nil, err + } + return activeProvider.dbList(namespace) +} + +func GetDbId(name, namespace string) (id int, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return -1, err + } + return activeProvider.dbID(name, namespace) +} + +func GetDbSeparator(name, namespace string) (separator string, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return "", err + } + return activeProvider.dbSeparator(name, namespace) +} + +func GetDbSock(name, namespace string) (socket string, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return "", err + } + return activeProvider.dbSocket(name, namespace) +} + +func GetDbHostName(name, namespace string) (hostname string, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return "", err + } + return activeProvider.dbHostname(name, namespace) +} + +func GetDbPort(name, namespace string) (port int, err error) { + defer catchException(&err) + if err := DbInit(); err != nil { + return -1, err + } + return activeProvider.dbPort(name, namespace) +} + +func GetDbTcpAddr(name, namespace string) (address string, err error) { + defer catchException(&err) + hostname, err := GetDbHostName(name, namespace) + if err != nil { + return "", err + } + port, err := GetDbPort(name, namespace) + if err != nil { + return "", err + } + return hostname + ":" + strconv.Itoa(port), nil +} + +func catchException(err *error) { + if recovered := recover(); recovered != nil { + *err = fmt.Errorf("%v", recovered) + } +} diff --git a/internal/adaptors/dbconfig/provider_pure.go b/internal/adaptors/dbconfig/provider_pure.go new file mode 100644 index 000000000..a1c3d863f --- /dev/null +++ b/internal/adaptors/dbconfig/provider_pure.go @@ -0,0 +1,147 @@ +//go:build pure + +package dbconfig + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "sort" +) + +const ( + defaultDatabaseConfigFile = ConfigFile + defaultGlobalConfigFile = GlobalConfigFile +) + +var ( + databaseConfigFile = defaultDatabaseConfigFile + globalConfigFile = defaultGlobalConfigFile + activeProvider provider = &fileProvider{} +) + +type fileProvider struct { + config databaseConfig +} + +type databaseConfig struct { + Instances map[string]instanceConfig `json:"INSTANCES"` + Databases map[string]databaseEntry `json:"DATABASES"` + Includes []json.RawMessage `json:"INCLUDES"` +} + +type instanceConfig struct { + Hostname string `json:"hostname"` + Port int `json:"port"` + UnixSocketPath string `json:"unix_socket_path"` +} + +type databaseEntry struct { + ID int `json:"id"` + Separator string `json:"separator"` + Instance string `json:"instance"` +} + +func (p *fileProvider) initialize() error { + if _, err := os.Stat(globalConfigFile); err == nil { + return fmt.Errorf("global database configuration is not supported by the pure provider") + } else if !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("inspect global database configuration: %w", err) + } + + data, err := os.ReadFile(databaseConfigFile) + if err != nil { + return fmt.Errorf("read database configuration: %w", err) + } + if err := json.Unmarshal(data, &p.config); err != nil { + return fmt.Errorf("parse database configuration: %w", err) + } + if len(p.config.Includes) > 0 { + return fmt.Errorf("database configuration INCLUDES are not supported by the pure provider") + } + return nil +} + +func (p *fileProvider) reset() error { + p.config = databaseConfig{} + return nil +} + +func (p *fileProvider) namespaces() ([]string, error) { + return []string{DefaultNamespace}, nil +} + +func (p *fileProvider) dbList(namespace string) ([]string, error) { + if err := validateNamespace(namespace); err != nil { + return nil, err + } + names := make([]string, 0, len(p.config.Databases)) + for name := range p.config.Databases { + names = append(names, name) + } + sort.Strings(names) + return names, nil +} + +func (p *fileProvider) dbID(name, namespace string) (int, error) { + entry, _, err := p.lookup(name, namespace) + if err != nil { + return -1, err + } + return entry.ID, nil +} + +func (p *fileProvider) dbSeparator(name, namespace string) (string, error) { + entry, _, err := p.lookup(name, namespace) + if err != nil { + return "", err + } + return entry.Separator, nil +} + +func (p *fileProvider) dbSocket(name, namespace string) (string, error) { + _, instance, err := p.lookup(name, namespace) + if err != nil { + return "", err + } + return instance.UnixSocketPath, nil +} + +func (p *fileProvider) dbHostname(name, namespace string) (string, error) { + _, instance, err := p.lookup(name, namespace) + if err != nil { + return "", err + } + return instance.Hostname, nil +} + +func (p *fileProvider) dbPort(name, namespace string) (int, error) { + _, instance, err := p.lookup(name, namespace) + if err != nil { + return -1, err + } + return instance.Port, nil +} + +func (p *fileProvider) lookup(name, namespace string) (databaseEntry, instanceConfig, error) { + if err := validateNamespace(namespace); err != nil { + return databaseEntry{}, instanceConfig{}, err + } + entry, ok := p.config.Databases[name] + if !ok { + return databaseEntry{}, instanceConfig{}, fmt.Errorf("database %q not present in standalone database configuration", name) + } + instance, ok := p.config.Instances[entry.Instance] + if !ok { + return databaseEntry{}, instanceConfig{}, fmt.Errorf("instance %q for database %q not present in standalone database configuration", entry.Instance, name) + } + return entry, instance, nil +} + +func validateNamespace(namespace string) error { + if namespace != DefaultNamespace { + return fmt.Errorf("namespace %q not present in standalone database configuration", namespace) + } + return nil +} diff --git a/internal/adaptors/dbconfig/provider_pure_test.go b/internal/adaptors/dbconfig/provider_pure_test.go new file mode 100644 index 000000000..8aaf74360 --- /dev/null +++ b/internal/adaptors/dbconfig/provider_pure_test.go @@ -0,0 +1,213 @@ +//go:build pure + +package dbconfig + +import ( + "os" + "path/filepath" + "reflect" + "slices" + "strings" + "testing" +) + +func TestPureProviderReadsDatabaseConfig(t *testing.T) { + usePureConfig(t, filepath.Join("testdata", "database_config.json")) + runProviderContract(t, providerContract{ + database: "CONFIG_DB", + namespace: DefaultNamespace, + id: 42, + separator: "~", + socket: "/tmp/standalone-redis.sock", + address: "db.example:6388", + }) +} + +func TestPureProviderDescribesStandaloneNamespace(t *testing.T) { + usePureConfig(t, filepath.Join("testdata", "database_config.json")) + + namespace, err := GetDbDefaultNamespace() + if err != nil { + t.Fatalf("GetDbDefaultNamespace() error = %v", err) + } + if namespace != DefaultNamespace { + t.Errorf("GetDbDefaultNamespace() = %q, want %q", namespace, DefaultNamespace) + } + + namespaces, err := GetDbAllNamespaces() + if err != nil { + t.Fatalf("GetDbAllNamespaces() error = %v", err) + } + if !reflect.DeepEqual(namespaces, []string{DefaultNamespace}) { + t.Errorf("GetDbAllNamespaces() = %v, want default namespace", namespaces) + } + + nonDefault, err := GetDbNonDefaultNamespaces() + if err != nil { + t.Fatalf("GetDbNonDefaultNamespaces() error = %v", err) + } + if len(nonDefault) != 0 { + t.Errorf("GetDbNonDefaultNamespaces() = %v, want none", nonDefault) + } + + multiNamespace, err := CheckDbMultiNamespace() + if err != nil { + t.Fatalf("CheckDbMultiNamespace() error = %v", err) + } + if multiNamespace { + t.Error("CheckDbMultiNamespace() = true, want false") + } + + databases, err := GetDbList(DefaultNamespace) + if err != nil { + t.Fatalf("GetDbList() error = %v", err) + } + wantDatabases := []string{"CONFIG_DB", "STATE_DB"} + if !reflect.DeepEqual(databases, wantDatabases) { + t.Errorf("GetDbList() = %v, want %v", databases, wantDatabases) + } +} + +func TestPureProviderRejectsUnknownDatabaseAndNamespace(t *testing.T) { + usePureConfig(t, filepath.Join("testdata", "database_config.json")) + + if _, err := GetDbId("UNKNOWN_DB", DefaultNamespace); err == nil || !strings.Contains(err.Error(), `database "UNKNOWN_DB"`) { + t.Errorf("GetDbId() error = %v, want unknown database error", err) + } + if _, err := GetDbId("CONFIG_DB", "asic0"); err == nil || !strings.Contains(err.Error(), `namespace "asic0"`) { + t.Errorf("GetDbId() error = %v, want unsupported namespace error", err) + } + + namespace, found, err := GetDbNamespaceFromTarget(DefaultNamespace) + if err != nil { + t.Fatalf("GetDbNamespaceFromTarget(default) error = %v", err) + } + if !found || namespace != DefaultNamespace { + t.Errorf("GetDbNamespaceFromTarget(default) = %q, %t; want default, true", namespace, found) + } + + namespace, found, err = GetDbNamespaceFromTarget("asic0") + if err != nil { + t.Fatalf("GetDbNamespaceFromTarget(asic0) error = %v", err) + } + if found || namespace != "" { + t.Errorf("GetDbNamespaceFromTarget(asic0) = %q, %t; want empty, false", namespace, found) + } +} + +func TestPureProviderRejectsGlobalConfiguration(t *testing.T) { + usePureConfig(t, filepath.Join("testdata", "database_config.json")) + globalConfigFile = filepath.Join("..", "..", "..", "testdata", "database_global.json") + + err := DbInit() + if err == nil || !strings.Contains(err.Error(), "global database configuration") { + t.Errorf("DbInit() error = %v, want unsupported global configuration error", err) + } +} + +func TestPureProviderRejectsIncludes(t *testing.T) { + configFile := filepath.Join(t.TempDir(), "database_config.json") + if err := os.WriteFile(configFile, []byte(`{"INCLUDES":["database_config.json"]}`), 0o600); err != nil { + t.Fatalf("WriteFile() error = %v", err) + } + usePureConfig(t, configFile) + + err := DbInit() + if err == nil || !strings.Contains(err.Error(), "INCLUDES") { + t.Errorf("DbInit() error = %v, want unsupported INCLUDES error", err) + } +} + +func TestDefaultTargetDoesNotInitializeProvider(t *testing.T) { + usePureConfig(t, filepath.Join(t.TempDir(), "missing-database-config.json")) + + namespace, found, err := GetDbNamespaceFromTarget(DefaultNamespace) + if err != nil { + t.Fatalf("GetDbNamespaceFromTarget(default) error = %v", err) + } + if !found || namespace != DefaultNamespace { + t.Errorf("GetDbNamespaceFromTarget(default) = %q, %t; want default, true", namespace, found) + } +} + +func usePureConfig(t *testing.T, path string) { + t.Helper() + databaseConfigFile = path + globalConfigFile = filepath.Join(t.TempDir(), "missing-global-config.json") + t.Cleanup(func() { + databaseConfigFile = defaultDatabaseConfigFile + globalConfigFile = defaultGlobalConfigFile + }) + if err := Init(); err != nil { + t.Fatalf("Init() error = %v", err) + } +} + +type providerContract struct { + database string + namespace string + id int + separator string + socket string + address string +} + +func runProviderContract(t *testing.T, contract providerContract) { + t.Helper() + + namespace, err := GetDbDefaultNamespace() + if err != nil { + t.Fatalf("GetDbDefaultNamespace() error = %v", err) + } + if namespace != contract.namespace { + t.Errorf("GetDbDefaultNamespace() = %q, want %q", namespace, contract.namespace) + } + + namespaces, err := GetDbAllNamespaces() + if err != nil { + t.Fatalf("GetDbAllNamespaces() error = %v", err) + } + if !slices.Contains(namespaces, contract.namespace) { + t.Errorf("GetDbAllNamespaces() = %v, want namespace %q", namespaces, contract.namespace) + } + + databases, err := GetDbList(contract.namespace) + if err != nil { + t.Fatalf("GetDbList() error = %v", err) + } + if !slices.Contains(databases, contract.database) { + t.Errorf("GetDbList() = %v, want database %q", databases, contract.database) + } + + id, err := GetDbId(contract.database, contract.namespace) + if err != nil { + t.Fatalf("GetDbId() error = %v", err) + } + if id != contract.id { + t.Errorf("GetDbId() = %d, want %d", id, contract.id) + } + + separator, err := GetDbSeparator(contract.database, contract.namespace) + if err != nil { + t.Fatalf("GetDbSeparator() error = %v", err) + } + if separator != contract.separator { + t.Errorf("GetDbSeparator() = %q, want %q", separator, contract.separator) + } + + socket, err := GetDbSock(contract.database, contract.namespace) + if err != nil { + t.Fatalf("GetDbSock() error = %v", err) + } + if socket != contract.socket { + t.Errorf("GetDbSock() = %q, want %q", socket, contract.socket) + } + + address, err := GetDbTcpAddr(contract.database, contract.namespace) + if err != nil { + t.Fatalf("GetDbTcpAddr() error = %v", err) + } + if address != contract.address { + t.Errorf("GetDbTcpAddr() = %q, want %q", address, contract.address) + } +} diff --git a/internal/adaptors/dbconfig/provider_swss.go b/internal/adaptors/dbconfig/provider_swss.go new file mode 100644 index 000000000..9d71a69eb --- /dev/null +++ b/internal/adaptors/dbconfig/provider_swss.go @@ -0,0 +1,75 @@ +//go:build !pure + +package dbconfig + +import ( + "errors" + "fmt" + "os" + + "github.com/sonic-net/sonic-gnmi/swsscommon" +) + +var activeProvider provider = swssProvider{} + +type swssProvider struct{} + +func (swssProvider) initialize() error { + if _, err := os.Stat(GlobalConfigFile); err == nil { + if !swsscommon.SonicDBConfigIsGlobalInit() { + swsscommon.SonicDBConfigInitializeGlobalConfig() + } + } else if !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("inspect global database configuration: %w", err) + } else if !swsscommon.SonicDBConfigIsInit() { + swsscommon.SonicDBConfigInitialize() + } + return nil +} + +func (swssProvider) reset() error { + swsscommon.SonicDBConfigReset() + return nil +} + +func (swssProvider) namespaces() ([]string, error) { + values := swsscommon.SonicDBConfigGetNamespaces() + defer swsscommon.DeleteVectorString(values) + + namespaces := make([]string, 0, int(values.Size())) + for i := 0; i < int(values.Size()); i++ { + namespaces = append(namespaces, values.Get(i)) + } + return namespaces, nil +} + +func (swssProvider) dbList(string) ([]string, error) { + values := swsscommon.SonicDBConfigGetDbList() + defer swsscommon.DeleteVectorString(values) + + databases := make([]string, 0, int(values.Size())) + for i := 0; i < int(values.Size()); i++ { + databases = append(databases, values.Get(i)) + } + return databases, nil +} + +func (swssProvider) dbID(name, namespace string) (int, error) { + return swsscommon.SonicDBConfigGetDbId(name, namespace), nil +} + +func (swssProvider) dbSeparator(name, namespace string) (string, error) { + return swsscommon.SonicDBConfigGetSeparator(name, namespace), nil +} + +func (swssProvider) dbSocket(name, namespace string) (string, error) { + return swsscommon.SonicDBConfigGetDbSock(name, namespace), nil +} + +func (swssProvider) dbHostname(name, namespace string) (string, error) { + return swsscommon.SonicDBConfigGetDbHostname(name, namespace), nil +} + +func (swssProvider) dbPort(name, namespace string) (int, error) { + return swsscommon.SonicDBConfigGetDbPort(name, namespace), nil +} diff --git a/internal/adaptors/dbconfig/testdata/database_config.json b/internal/adaptors/dbconfig/testdata/database_config.json new file mode 100644 index 000000000..e59c93125 --- /dev/null +++ b/internal/adaptors/dbconfig/testdata/database_config.json @@ -0,0 +1,22 @@ +{ + "INSTANCES": { + "standalone": { + "hostname": "db.example", + "port": 6388, + "unix_socket_path": "/tmp/standalone-redis.sock" + } + }, + "DATABASES": { + "CONFIG_DB": { + "id": 42, + "separator": "~", + "instance": "standalone" + }, + "STATE_DB": { + "id": 43, + "separator": "^", + "instance": "standalone" + } + }, + "VERSION": "1.0" +} diff --git a/pure.mk b/pure.mk index 2af6ed6f8..4537155bb 100644 --- a/pure.mk +++ b/pure.mk @@ -7,6 +7,10 @@ # Go configuration GO ?= go GOROOT ?= $(shell $(GO) env GOROOT) +PURE_CGO_ENABLED ?= 0 +RACE_CGO_ENABLED ?= 1 +PURE_TAG ?= pure +PURE_GO_FLAGS := -tags=$(PURE_TAG) # Discover every package under the canonical pure roots. This makes purity a # path-based invariant instead of an allowlist that can omit new packages. @@ -64,17 +68,17 @@ vet: @echo "Running go vet on pure packages..." @set -e; for pkg in $(PACKAGES); do \ echo "Vetting $$pkg..."; \ - (cd $$pkg && $(GO) vet .); \ + (cd $$pkg && CGO_ENABLED=$(PURE_CGO_ENABLED) $(GO) vet $(PURE_GO_FLAGS) .); \ done # Test - run all tests with coverage .PHONY: test -test: +test: build-test @echo "Running tests for pure packages..." @set -e; for pkg in $(PACKAGES); do \ echo ""; \ echo "=== Testing $$pkg ==="; \ - (cd $$pkg && $(GO) test -gcflags="all=-N -l" -v -race -coverprofile=coverage.out -covermode=atomic .); \ + (cd $$pkg && CGO_ENABLED=$(RACE_CGO_ENABLED) $(GO) test $(PURE_GO_FLAGS) -gcflags="all=-N -l" -v -race -coverprofile=coverage.out -covermode=atomic .); \ if [ -f $$pkg/coverage.out ]; then \ echo "Coverage for $$pkg:"; \ (cd $$pkg && $(GO) tool cover -func=coverage.out); \ @@ -83,12 +87,12 @@ test: # Generate coverage files for Azure pipeline integration .PHONY: azure-coverage -azure-coverage: +azure-coverage: build-test @echo "Generating coverage files for Azure pipeline..." @set -e; for pkg in $(PACKAGES); do \ echo "Testing $$pkg..."; \ pkgname=$$(echo $$pkg | tr '/' '-'); \ - $(GO) test -gcflags="all=-N -l" -race -coverprofile=coverage-pure-$$pkgname.txt -covermode=atomic -v ./$$pkg; \ + CGO_ENABLED=$(RACE_CGO_ENABLED) $(GO) test $(PURE_GO_FLAGS) -gcflags="all=-N -l" -race -coverprofile=coverage-pure-$$pkgname.txt -covermode=atomic -v ./$$pkg; \ done @echo "Coverage files generated for Azure pipeline" @@ -134,10 +138,11 @@ coverage-xml: test # Build test - ensure the package builds .PHONY: build-test build-test: - @echo "Testing build of pure packages..." + @echo "Testing build of pure packages and tests..." @set -e; for pkg in $(PACKAGES); do \ echo "Building $$pkg..."; \ - (cd $$pkg && $(GO) build -v .); \ + (cd $$pkg && CGO_ENABLED=$(PURE_CGO_ENABLED) $(GO) build $(PURE_GO_FLAGS) -v .); \ + (cd $$pkg && CGO_ENABLED=$(PURE_CGO_ENABLED) $(GO) test $(PURE_GO_FLAGS) -c -o /dev/null .); \ done # Lint check using basic go tools @@ -151,7 +156,7 @@ bench: @echo "Running benchmarks for pure packages..." @set -e; for pkg in $(PACKAGES); do \ echo "Benchmarking $$pkg..."; \ - (cd $$pkg && $(GO) test -bench=. -benchmem .); \ + (cd $$pkg && CGO_ENABLED=$(PURE_CGO_ENABLED) $(GO) test $(PURE_GO_FLAGS) -bench=. -benchmem .); \ done # Module verification @@ -173,14 +178,32 @@ security: @set -e; if command -v gosec >/dev/null 2>&1; then \ for pkg in $(PACKAGES); do \ echo "Scanning $$pkg..."; \ - (cd $$pkg && gosec .); \ + (cd $$pkg && gosec -tags=$(PURE_TAG) .); \ done; \ else \ echo "gosec not available, skipping security scan"; \ echo "Install with: go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest"; \ fi -# List vanilla packages +# Keep pure build constraints under internal/adaptors rather than business logic. +.PHONY: check-tags +check-tags: + @echo "Checking pure build-tag placement..." + @set -e; \ + files=$$(find $(PURE_ROOTS) -type f -name '*.go' -exec grep -l '^//go:build .*pure' {} + 2>/dev/null || true); \ + for file in $$files; do \ + case "$$file" in \ + internal/adaptors/*/provider_*.go) \ + relative=$${file#internal/adaptors/}; \ + case "$$relative" in */*/*) ;; *) continue ;; esac; \ + ;; \ + esac; \ + echo "Pure build tag is outside a direct internal adaptor provider: $$file"; \ + exit 1; \ + done + @echo "Pure build-tag placement is clean." + +# List pure packages .PHONY: list-packages list-packages: @echo "Pure packages:" @@ -196,7 +219,7 @@ list-packages: # Full CI pipeline .PHONY: ci -ci: clean lint build-test test +ci: clean check-tags lint build-test test @echo "" @echo "=============================================" @echo "✅ Pure CI completed successfully!" @@ -219,7 +242,7 @@ ci: clean lint build-test test # Note: The Azure pipeline now calls gotestsum directly with set -euo pipefail # This target is kept for local testing convenience .PHONY: junit-xml -junit-xml: clean +junit-xml: clean check-tags build-test @echo "Installing gotestsum for JUnit XML generation..." @if ! command -v gotestsum >/dev/null 2>&1; then \ $(GO) install gotest.tools/gotestsum@v1.11.0; \ @@ -234,9 +257,9 @@ junit-xml: clean @echo "Running pure package tests with JUnit XML output..." @mkdir -p test-results @export PATH=$(PATH):$(shell $(GO) env GOPATH)/bin && \ - gotestsum --junitfile test-results/junit-pure.xml \ + CGO_ENABLED=$(RACE_CGO_ENABLED) gotestsum --junitfile test-results/junit-pure.xml \ --format testname \ - -- -gcflags="all=-N -l" -v -race \ + -- $(PURE_GO_FLAGS) -gcflags="all=-N -l" -v -race \ -coverprofile=test-results/coverage-pure.txt \ -covermode=atomic \ $(addprefix ./,$(PACKAGES)) @@ -262,7 +285,7 @@ junit-xml: clean # Quick check for development .PHONY: quick -quick: fmt-check vet build-test +quick: check-tags fmt-check vet build-test @echo "Quick validation complete for pure packages" # Help target @@ -288,6 +311,7 @@ help: @echo " build-test - Test package builds" @echo " bench - Run benchmarks" @echo " security - Run security scan (requires gosec)" + @echo " check-tags - Restrict pure tags to internal adaptors" @echo " mod-verify - Verify go modules" @echo " list-packages - List pure packages" @echo " clean - Clean build artifacts" diff --git a/sonic_db_config/db_config.go b/sonic_db_config/db_config.go index deb7b1de5..c204c2fb9 100644 --- a/sonic_db_config/db_config.go +++ b/sonic_db_config/db_config.go @@ -4,15 +4,15 @@ package dbconfig import ( "fmt" + internaldbconfig "github.com/sonic-net/sonic-gnmi/internal/adaptors/dbconfig" "github.com/sonic-net/sonic-gnmi/swsscommon" - "os" "strconv" ) const ( - SONIC_DB_GLOBAL_CONFIG_FILE string = "/var/run/redis/sonic-db/database_global.json" - SONIC_DB_CONFIG_FILE string = "/var/run/redis/sonic-db/database_config.json" - SONIC_DEFAULT_NAMESPACE string = "" + SONIC_DB_GLOBAL_CONFIG_FILE string = internaldbconfig.GlobalConfigFile + SONIC_DB_CONFIG_FILE string = internaldbconfig.ConfigFile + SONIC_DEFAULT_NAMESPACE string = internaldbconfig.DefaultNamespace SONIC_DEFAULT_CONTAINER string = "" ) @@ -26,185 +26,87 @@ func CatchException(err *error) { } func GetDbDefaultNamespace() (ns string, err error) { - return SONIC_DEFAULT_NAMESPACE, nil + return internaldbconfig.GetDbDefaultNamespace() } func CheckDbMultiNamespace() (ret bool, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return false, err - } + if err := DbInit(); err != nil { + return false, err } - defer CatchException(&err) - ns_vec := swsscommon.SonicDBConfigGetNamespaces() - defer func() { - swsscommon.DeleteVectorString(ns_vec) - }() - length := int(ns_vec.Size()) - // If there are more than one namespaces, this means that SONiC is using multinamespace - return length > 1, err + return internaldbconfig.CheckDbMultiNamespace() } func GetDbNonDefaultNamespaces() (ns_list []string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return ns_list, err - } + if err := DbInit(); err != nil { + return nil, err } - defer CatchException(&err) - ns_vec := swsscommon.SonicDBConfigGetNamespaces() - defer func() { - swsscommon.DeleteVectorString(ns_vec) - }() - // Translate from vector to array - length := int(ns_vec.Size()) - for i := 0; i < length; i += 1 { - ns := ns_vec.Get(i) - if ns == SONIC_DEFAULT_NAMESPACE { - continue - } - ns_list = append(ns_list, ns) - } - return ns_list, err + return internaldbconfig.GetDbNonDefaultNamespaces() } func GetDbAllNamespaces() (ns_list []string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return ns_list, err - } + if err := DbInit(); err != nil { + return nil, err } - defer CatchException(&err) - ns_vec := swsscommon.SonicDBConfigGetNamespaces() - defer func() { - swsscommon.DeleteVectorString(ns_vec) - }() - // Translate from vector to array - length := int(ns_vec.Size()) - for i := 0; i < length; i += 1 { - ns := ns_vec.Get(i) - ns_list = append(ns_list, ns) - } - return ns_list, err + return internaldbconfig.GetDbAllNamespaces() } func GetDbNamespaceFromTarget(target string) (ns string, ret bool, err error) { - ns, _ = GetDbDefaultNamespace() - if target == ns { + if target == SONIC_DEFAULT_NAMESPACE { return target, true, nil } - ns_list, err := GetDbNonDefaultNamespaces() - if err != nil { + if err := DbInit(); err != nil { return "", false, err } - for _, ns := range ns_list { - if target == ns { - return target, true, nil - } - } - return "", false, nil + return internaldbconfig.GetDbNamespaceFromTarget(target) } func GetDbList(ns string) (db_list []string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return db_list, err - } - } - defer CatchException(&err) - db_vec := swsscommon.SonicDBConfigGetDbList() - defer func() { - swsscommon.DeleteVectorString(db_vec) - }() - // Translate from vector to array - length := int(db_vec.Size()) - for i := 0; i < length; i += 1 { - ns := db_vec.Get(i) - db_list = append(db_list, ns) + if err := DbInit(); err != nil { + return nil, err } - return db_list, err + return internaldbconfig.GetDbList(ns) } func GetDbSeparator(db_name string, ns string) (separator string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return "", err - } + if err := DbInit(); err != nil { + return "", err } - defer CatchException(&err) - separator = swsscommon.SonicDBConfigGetSeparator(db_name, ns) - return separator, err + return internaldbconfig.GetDbSeparator(db_name, ns) } func GetDbId(db_name string, ns string) (id int, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return -1, err - } + if err := DbInit(); err != nil { + return -1, err } - defer CatchException(&err) - id = swsscommon.SonicDBConfigGetDbId(db_name, ns) - return id, err + return internaldbconfig.GetDbId(db_name, ns) } func GetDbSock(db_name string, ns string) (unix_socket_path string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return "", err - } + if err := DbInit(); err != nil { + return "", err } - defer CatchException(&err) - unix_socket_path = swsscommon.SonicDBConfigGetDbSock(db_name, ns) - return unix_socket_path, err + return internaldbconfig.GetDbSock(db_name, ns) } func GetDbHostName(db_name string, ns string) (hostname string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return "", err - } + if err := DbInit(); err != nil { + return "", err } - defer CatchException(&err) - hostname = swsscommon.SonicDBConfigGetDbHostname(db_name, ns) - return hostname, err + return internaldbconfig.GetDbHostName(db_name, ns) } func GetDbPort(db_name string, ns string) (port int, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return -1, err - } + if err := DbInit(); err != nil { + return -1, err } - defer CatchException(&err) - port = swsscommon.SonicDBConfigGetDbPort(db_name, ns) - return port, err + return internaldbconfig.GetDbPort(db_name, ns) } func GetDbTcpAddr(db_name string, ns string) (addr string, err error) { - if !sonic_db_init { - err = DbInit() - if err != nil { - return "", err - } - } - hostname, err := GetDbHostName(db_name, ns) - if err != nil { + if err := DbInit(); err != nil { return "", err } - port, err := GetDbPort(db_name, ns) - if err != nil { - return "", err - } - return hostname + ":" + strconv.Itoa(port), err + return internaldbconfig.GetDbTcpAddr(db_name, ns) } func CheckDbMultiInstance() (ret bool, err error) { @@ -390,25 +292,15 @@ func DbInit() (err error) { return nil } defer CatchException(&err) - if _, ierr := os.Stat(SONIC_DB_GLOBAL_CONFIG_FILE); ierr == nil || os.IsExist(ierr) { - // If there's global config file, invoke SonicDBConfigInitializeGlobalConfig - if !swsscommon.SonicDBConfigIsGlobalInit() { - swsscommon.SonicDBConfigInitializeGlobalConfig() - } - } else { - // If there's no global config file, invoke SonicDBConfigInitialize - if !swsscommon.SonicDBConfigIsInit() { - swsscommon.SonicDBConfigInitialize() - } + if err := internaldbconfig.DbInit(); err != nil { + return err } sonic_db_init = true - return err + return nil } func Init() (err error) { sonic_db_init = false defer CatchException(&err) - // Clear database configuration - swsscommon.SonicDBConfigReset() - return err + return internaldbconfig.Init() }