refactor: restructure library to expose public API directly

feat: add new helper methods for RAC operations
docs: add comprehensive library usage documentation
chore: move example to examples/library_usage directory
This commit is contained in:
2025-08-04 12:03:45 +03:00
parent ccbe0c669d
commit 46261f066b
7 changed files with 1152 additions and 678 deletions

252
LIBRARY_USAGE.md Normal file
View File

@@ -0,0 +1,252 @@
# Использование benadis-rac как библиотеки
После рефакторинга библиотека benadis-rac может использоваться без импорта внутренних пакетов.
## Установка
```bash
go get git.benadis.ru/gitops/benadis-rac
```
## Импорт
```go
import "git.benadis.ru/gitops/benadis-rac"
```
## Основное использование
### Способ 1: Использование функции ManageServiceMode (рекомендуется)
```go
package main
import (
"context"
"time"
benadis_rac "git.benadis.ru/gitops/benadis-rac"
)
func main() {
// Создаем конфигурацию
config := &benadis_rac.Config{
// RAC configuration
RACPath: "C:\\Program Files\\1cv8\\8.3.25.1257\\bin\\rac.exe",
ConnectionTimeout: 30 * time.Second,
CommandTimeout: 60 * time.Second,
RetryCount: 3,
RetryDelay: 5 * time.Second,
// Server configuration
ServerHost: "localhost",
ServerPort: 1541,
RACPort: 1545,
ServerName: "server1c",
BaseName: "test_base",
// Authentication
ClusterAdmin: "admin",
ClusterAdminPassword: "password",
DBAdmin: "dbadmin",
DBAdminPassword: "dbpassword",
// Service mode settings
DeniedMessage: "Система находится на техническом обслуживании",
PermissionCode: "maintenance",
}
// Устанавливаем значения по умолчанию
config.SetDefaults()
// Валидируем конфигурацию
if err := config.Validate(); err != nil {
panic(err)
}
// Создаем логгер
logger := benadis_rac.NewLogger("info")
// Создаем контекст
ctx := context.Background()
// Включаем сервисный режим
err := benadis_rac.ManageServiceMode(ctx, config, logger, true)
if err != nil {
panic(err)
}
// Выключаем сервисный режим
err = benadis_rac.ManageServiceMode(ctx, config, logger, false)
if err != nil {
panic(err)
}
}
```
### Способ 2: Использование клиента
```go
package main
import (
"context"
benadis_rac "git.benadis.ru/gitops/benadis-rac"
)
func main() {
// Создаем конфигурацию (аналогично способу 1)
config := &benadis_rac.Config{
// ... настройки конфигурации
}
config.SetDefaults()
// Создаем клиент
client, err := benadis_rac.NewClient(config)
if err != nil {
panic(err)
}
ctx := context.Background()
// Проверяем текущий статус
enabled, err := client.GetServiceModeStatus(ctx)
if err != nil {
panic(err)
}
// Включаем сервисный режим
err = client.EnableServiceMode(ctx)
if err != nil {
panic(err)
}
// Выключаем сервисный режим
err = client.DisableServiceMode(ctx)
if err != nil {
panic(err)
}
}
```
## Структура конфигурации
```go
type Config struct {
// RAC configuration
RACPath string // Путь к исполняемому файлу RAC
ConnectionTimeout time.Duration // Таймаут подключения
CommandTimeout time.Duration // Таймаут выполнения команды
RetryCount int // Количество повторных попыток
RetryDelay time.Duration // Задержка между попытками
// Server configuration
ServerHost string // Хост сервера 1C
ServerPort int // Порт сервера 1C
RACPort int // Порт RAC
ServerName string // Имя сервера
BaseName string // Имя информационной базы
// Authentication
ClusterAdmin string // Администратор кластера
ClusterAdminPassword string // Пароль администратора кластера
DBAdmin string // Администратор ИБ
DBAdminPassword string // Пароль администратора ИБ
// Service mode settings
DeniedMessage string // Сообщение при блокировке
PermissionCode string // Код разрешения
}
```
## Интерфейс логгера
Вы можете использовать встроенный логгер или реализовать свой:
```go
type Logger interface {
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
DebugCommand(msg string, command []string)
}
// Создание встроенного логгера
logger := benadis_rac.NewLogger("info") // debug, info, warn, error
```
## Методы управления
### ManageServiceMode
Основная функция для управления сервисным режимом:
```go
func ManageServiceMode(ctx context.Context, config *Config, logger Logger, enable bool) error
```
### Методы клиента
- `EnableServiceMode(ctx context.Context) error` - включить сервисный режим
- `DisableServiceMode(ctx context.Context) error` - выключить сервисный режим
- `GetServiceModeStatus(ctx context.Context) (bool, error)` - получить статус сервисного режима
## Значения по умолчанию
```go
const (
DefaultDeniedMessage = "Техническое обслуживание. Попробуйте позже."
DefaultPermissionCode = "service-mode"
DefaultRACPort = 1545
DefaultConnectionTimeout = 30 * time.Second
DefaultCommandTimeout = 60 * time.Second
DefaultRetryCount = 3
DefaultRetryDelay = 5 * time.Second
)
```
## Валидация конфигурации
Обязательные поля:
- `ServerHost` - хост сервера 1C
- `RACPath` - путь к исполняемому файлу RAC
- `BaseName` - имя информационной базы
- `ClusterAdmin` - администратор кластера
- `DBAdmin` - администратор ИБ
## Обработка ошибок
Все методы возвращают детализированные ошибки с контекстом:
```go
if err := client.EnableServiceMode(ctx); err != nil {
log.Printf("Failed to enable service mode: %v", err)
}
```
## Миграция с предыдущей версии
### Было (с внутренними пакетами):
```go
import (
"git.benadis.ru/gitops/benadis-rac/internal/config"
"git.benadis.ru/gitops/benadis-rac/internal/logger"
"git.benadis.ru/gitops/benadis-rac/internal/service"
)
// Загрузка из файлов конфигурации
appConfig, err := config.LoadConfig("config.yaml", "secret.yaml", "project.yaml")
log := logger.NewLogger("info")
svc := service.NewServiceModeService(appConfig, log)
```
### Стало (только публичный API):
```go
import benadis_rac "git.benadis.ru/gitops/benadis-rac"
// Прямая передача значений в структуру
config := &benadis_rac.Config{
RACPath: "path/to/rac",
ServerHost: "localhost",
// ... другие поля
}
logger := benadis_rac.NewLogger("info")
benadis_rac.ManageServiceMode(ctx, config, logger, true)
```

View File

@@ -4,12 +4,51 @@ package benadis_rac
import (
"context"
"fmt"
"git.benadis.ru/gitops/benadis-rac/internal/config"
"git.benadis.ru/gitops/benadis-rac/internal/logger"
"git.benadis.ru/gitops/benadis-rac/internal/service"
"log/slog"
"os"
"os/exec"
"path"
"strings"
"time"
"unicode/utf8"
)
// Logger интерфейс для логирования
type Logger interface {
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
DebugCommand(msg string, command []string)
}
// Config конфигурация для управления сервисным режимом
type Config struct {
// RAC configuration
RACPath string // Путь к исполняемому файлу RAC
ConnectionTimeout time.Duration // Таймаут подключения
CommandTimeout time.Duration // Таймаут выполнения команды
RetryCount int // Количество повторных попыток
RetryDelay time.Duration // Задержка между попытками
// Server configuration
ServerHost string // Хост сервера 1C
ServerPort int // Порт сервера 1C
RACPort int // Порт RAC
ServerName string // Имя сервера
BaseName string // Имя информационной базы
// Authentication
ClusterAdmin string // Администратор кластера
ClusterAdminPassword string // Пароль администратора кластера
DBAdmin string // Администратор ИБ
DBAdminPassword string // Пароль администратора ИБ
// Service mode settings
DeniedMessage string // Сообщение при блокировке
PermissionCode string // Код разрешения
}
// ServiceModeManager интерфейс для управления сервисным режимом
type ServiceModeManager interface {
// EnableServiceMode включает сервисный режим
@@ -24,101 +63,450 @@ type ServiceModeManager interface {
// Client клиент для работы с 1C сервисным режимом
type Client struct {
service *service.ServiceModeService
config *Config
logger Logger
}
// Config конфигурация для создания клиента
type Config struct {
ConfigPath string // Путь к файлу конфигурации
SecretPath string // Путь к файлу с секретами
ProjectPath string // Путь к файлу проекта
LogLevel string // Уровень логирования (Debug, Info, Warn, Error)
// SlogLogger реализация Logger с использованием slog
type SlogLogger struct {
logger *slog.Logger
}
// NewLogger создает новый логгер
func NewLogger(level string) Logger {
var logLevel slog.Level
switch strings.ToLower(level) {
case "debug":
logLevel = slog.LevelDebug
case "info":
logLevel = slog.LevelInfo
case "warn":
logLevel = slog.LevelWarn
case "error":
logLevel = slog.LevelError
default:
logLevel = slog.LevelInfo
}
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: logLevel,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.SourceKey {
source := a.Value.Any().(*slog.Source)
source.File = path.Base(source.File)
}
return a
},
})
return &SlogLogger{
logger: slog.New(handler),
}
}
func (l *SlogLogger) Debug(msg string, args ...any) {
l.logger.Debug(msg, args...)
}
func (l *SlogLogger) Info(msg string, args ...any) {
l.logger.Info(msg, args...)
}
func (l *SlogLogger) Warn(msg string, args ...any) {
l.logger.Warn(msg, args...)
}
func (l *SlogLogger) Error(msg string, args ...any) {
l.logger.Error(msg, args...)
}
func (l *SlogLogger) DebugCommand(msg string, command []string) {
maskedCommand := maskPasswords(command)
l.logger.Debug(msg, "command", strings.Join(maskedCommand, " "))
}
func maskPasswords(command []string) []string {
masked := make([]string, len(command))
copy(masked, command)
passwordFlags := []string{"--cluster-pwd", "--infobase-pwd"}
for i, arg := range masked {
for _, flag := range passwordFlags {
if arg == flag && i+1 < len(masked) {
masked[i+1] = "***"
break
}
}
}
return masked
}
// NewClient создает новый клиент для управления сервисным режимом
func NewClient(cfg Config) (*Client, error) {
if cfg.ConfigPath == "" {
cfg.ConfigPath = "config.yaml"
}
if cfg.SecretPath == "" {
cfg.SecretPath = "secret.yaml"
}
if cfg.ProjectPath == "" {
cfg.ProjectPath = "project.yaml"
}
if cfg.LogLevel == "" {
cfg.LogLevel = "Info"
}
// Загружаем конфигурацию
appConfig, err := config.LoadConfig(cfg.ConfigPath, cfg.SecretPath, cfg.ProjectPath)
if err != nil {
return nil, fmt.Errorf("failed to load config: %w", err)
}
// Переопределяем уровень логирования если указан
if appConfig.Project != nil && appConfig.Project.ServiceMode != nil {
appConfig.Project.ServiceMode.LogLevel = cfg.LogLevel
}
// Валидируем конфигурацию
if err := appConfig.Validate(); err != nil {
return nil, fmt.Errorf("config validation failed: %w", err)
func NewClient(config *Config) (*Client, error) {
if config == nil {
return nil, fmt.Errorf("config cannot be nil")
}
// Создаем логгер
logLevel := "Info"
if appConfig.Project != nil && appConfig.Project.ServiceMode != nil && appConfig.Project.ServiceMode.LogLevel != "" {
logLevel = appConfig.Project.ServiceMode.LogLevel
}
log := logger.NewLogger(logLevel)
// Создаем сервис
svc := service.NewServiceModeService(appConfig, log)
logger := NewLogger("info")
return &Client{
service: svc,
config: config,
logger: logger,
}, nil
}
// ManageServiceMode основная функция для управления сервисным режимом
func ManageServiceMode(ctx context.Context, config *Config, logger Logger, enable bool) error {
if config == nil {
return fmt.Errorf("config cannot be nil")
}
if logger == nil {
return fmt.Errorf("logger cannot be nil")
}
client := &Client{
config: config,
logger: logger,
}
if enable {
return client.EnableServiceMode(ctx)
}
return client.DisableServiceMode(ctx)
}
// EnableServiceMode включает сервисный режим
func (c *Client) EnableServiceMode(ctx context.Context) error {
return c.service.EnableServiceMode(ctx)
c.logger.Info("Starting service mode enablement")
// Получаем UUID кластера
clusterUUID, err := c.getClusterUUID(ctx)
if err != nil {
return fmt.Errorf("failed to get cluster UUID: %w", err)
}
// Получаем UUID информационной базы
infobaseUUID, err := c.getInfobaseUUID(ctx, clusterUUID)
if err != nil {
return fmt.Errorf("failed to get infobase UUID: %w", err)
}
// Включаем сервисный режим
if err := c.setServiceMode(ctx, clusterUUID, infobaseUUID, true); err != nil {
return fmt.Errorf("failed to enable service mode: %w", err)
}
// Завершаем все активные сессии
if err := c.terminateAllSessions(ctx, clusterUUID, infobaseUUID); err != nil {
c.logger.Warn("Failed to terminate all sessions", "error", err)
}
// Проверяем статус
if err := c.verifyServiceMode(ctx, clusterUUID, infobaseUUID, true); err != nil {
return fmt.Errorf("service mode verification failed: %w", err)
}
c.logger.Info("Service mode enabled successfully")
return nil
}
// DisableServiceMode выключает сервисный режим
func (c *Client) DisableServiceMode(ctx context.Context) error {
return c.service.DisableServiceMode(ctx)
c.logger.Info("Starting service mode disablement")
// Получаем UUID кластера
clusterUUID, err := c.getClusterUUID(ctx)
if err != nil {
return fmt.Errorf("failed to get cluster UUID: %w", err)
}
// Получаем UUID информационной базы
infobaseUUID, err := c.getInfobaseUUID(ctx, clusterUUID)
if err != nil {
return fmt.Errorf("failed to get infobase UUID: %w", err)
}
// Выключаем сервисный режим
if err := c.setServiceMode(ctx, clusterUUID, infobaseUUID, false); err != nil {
return fmt.Errorf("failed to disable service mode: %w", err)
}
// Проверяем статус
if err := c.verifyServiceMode(ctx, clusterUUID, infobaseUUID, false); err != nil {
return fmt.Errorf("service mode verification failed: %w", err)
}
c.logger.Info("Service mode disabled successfully")
return nil
}
// GetServiceModeStatus получает текущий статус сервисного режима
// Возвращает true если сервисный режим включен, false если выключен
func (c *Client) GetServiceModeStatus(ctx context.Context) (bool, error) {
return c.service.GetServiceModeStatus(ctx)
// Получаем UUID кластера
clusterUUID, err := c.getClusterUUID(ctx)
if err != nil {
return false, fmt.Errorf("failed to get cluster UUID: %w", err)
}
// NewClientFromConfig создает клиент из готовой конфигурации
func NewClientFromConfig(appConfig *config.AppConfig, logLevel string) (*Client, error) {
if logLevel != "" && appConfig.Project != nil && appConfig.Project.ServiceMode != nil {
appConfig.Project.ServiceMode.LogLevel = logLevel
// Получаем UUID информационной базы
infobaseUUID, err := c.getInfobaseUUID(ctx, clusterUUID)
if err != nil {
return false, fmt.Errorf("failed to get infobase UUID: %w", err)
}
// Валидируем конфигурацию
if err := appConfig.Validate(); err != nil {
return nil, fmt.Errorf("config validation failed: %w", err)
// Получаем информацию об информационной базе
args := []string{
c.config.RACPath,
"infobase", "info",
"--cluster=" + clusterUUID,
"--infobase=" + infobaseUUID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
"--infobase-user=" + c.config.DBAdmin,
"--infobase-pwd=" + c.config.DBAdminPassword,
c.getRACAddress(),
}
// Создаем логгер
logLevelToUse := "Info"
if appConfig.Project != nil && appConfig.Project.ServiceMode != nil && appConfig.Project.ServiceMode.LogLevel != "" {
logLevelToUse = appConfig.Project.ServiceMode.LogLevel
output, err := c.executeCommand(ctx, args)
if err != nil {
return false, fmt.Errorf("failed to get infobase info: %w", err)
}
log := logger.NewLogger(logLevelToUse)
// Создаем сервис
svc := service.NewServiceModeService(appConfig, log)
return &Client{
service: svc,
}, nil
// Проверяем статус сервисного режима
return strings.Contains(output, "sessions-deny") && strings.Contains(output, "on"), nil
}
// Константы
const (
DefaultDeniedMessage = "Техническое обслуживание. Попробуйте позже."
DefaultPermissionCode = "service-mode"
TechnicalMaintenanceMessage = "Техническое обслуживание"
UUIDLength = 36
UUIDDashCount = 4
DefaultRACPort = 1545
DefaultConnectionTimeout = 30 * time.Second
DefaultCommandTimeout = 60 * time.Second
DefaultRetryCount = 3
DefaultRetryDelay = 5 * time.Second
)
// getRACAddress возвращает адрес RAC сервера
func (c *Client) getRACAddress() string {
port := c.config.RACPort
if port == 0 {
port = DefaultRACPort
}
return fmt.Sprintf("%s:%d", c.config.ServerHost, port)
}
// executeCommand выполняет RAC команду с retry логикой
func (c *Client) executeCommand(ctx context.Context, args []string) (string, error) {
var lastErr error
retryCount := c.config.RetryCount
if retryCount == 0 {
retryCount = DefaultRetryCount
}
retryDelay := c.config.RetryDelay
if retryDelay == 0 {
retryDelay = DefaultRetryDelay
}
for attempt := 1; attempt <= retryCount; attempt++ {
output, err := c.executeCommandOnce(ctx, args)
if err == nil {
return output, nil
}
lastErr = err
c.logger.Warn("Command execution failed", "attempt", attempt, "error", err)
if attempt < retryCount {
c.logger.Info("Retrying after delay", "delay", retryDelay)
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(retryDelay):
}
}
}
return "", fmt.Errorf("command failed after %d attempts: %w", retryCount, lastErr)
}
// executeCommandOnce выполняет RAC команду один раз
func (c *Client) executeCommandOnce(ctx context.Context, args []string) (string, error) {
c.logger.DebugCommand("Executing RAC command", args)
cmdTimeout := c.config.CommandTimeout
if cmdTimeout == 0 {
cmdTimeout = DefaultCommandTimeout
}
cmdCtx, cancel := context.WithTimeout(ctx, cmdTimeout)
defer cancel()
cmd := exec.CommandContext(cmdCtx, args[0], args[1:]...)
output, err := cmd.Output()
if err != nil {
c.logger.Error("RAC command failed", "error", err, "output", string(output))
return "", fmt.Errorf("RAC command failed: %w, output: %s", err, string(output))
}
utf8Output := convertToUTF8(output)
c.logger.Debug("RAC command executed successfully", "output_length", len(utf8Output))
return utf8Output, nil
}
// convertToUTF8 конвертирует байты в UTF-8 строку
func convertToUTF8(data []byte) string {
if utf8.Valid(data) {
return string(data)
}
// Пытаемся декодировать как Windows-1251
runes := make([]rune, 0, len(data))
for _, b := range data {
if b < 128 {
runes = append(runes, rune(b))
} else {
// Простая таблица конвертации для основных символов Windows-1251
switch b {
case 0xC0: // А
runes = append(runes, 'А')
case 0xC1: // Б
runes = append(runes, 'Б')
case 0xC2: // В
runes = append(runes, 'В')
case 0xC3: // Г
runes = append(runes, 'Г')
case 0xC4: // Д
runes = append(runes, 'Д')
case 0xC5: // Е
runes = append(runes, 'Е')
case 0xC6: // Ж
runes = append(runes, 'Ж')
case 0xC7: // З
runes = append(runes, 'З')
case 0xC8: // И
runes = append(runes, 'И')
case 0xC9: // Й
runes = append(runes, 'Й')
case 0xCA: // К
runes = append(runes, 'К')
case 0xCB: // Л
runes = append(runes, 'Л')
case 0xCC: // М
runes = append(runes, 'М')
case 0xCD: // Н
runes = append(runes, 'Н')
case 0xCE: // О
runes = append(runes, 'О')
case 0xCF: // П
runes = append(runes, 'П')
case 0xD0: // Р
runes = append(runes, 'Р')
case 0xD1: // С
runes = append(runes, 'С')
case 0xD2: // Т
runes = append(runes, 'Т')
case 0xD3: // У
runes = append(runes, 'У')
case 0xD4: // Ф
runes = append(runes, 'Ф')
case 0xD5: // Х
runes = append(runes, 'Х')
case 0xD6: // Ц
runes = append(runes, 'Ц')
case 0xD7: // Ч
runes = append(runes, 'Ч')
case 0xD8: // Ш
runes = append(runes, 'Ш')
case 0xD9: // Щ
runes = append(runes, 'Щ')
case 0xDA: // Ъ
runes = append(runes, 'Ъ')
case 0xDB: // Ы
runes = append(runes, 'Ы')
case 0xDC: // Ь
runes = append(runes, 'Ь')
case 0xDD: // Э
runes = append(runes, 'Э')
case 0xDE: // Ю
runes = append(runes, 'Ю')
case 0xDF: // Я
runes = append(runes, 'Я')
case 0xE0: // а
runes = append(runes, 'а')
case 0xE1: // б
runes = append(runes, 'б')
case 0xE2: // в
runes = append(runes, 'в')
case 0xE3: // г
runes = append(runes, 'г')
case 0xE4: // д
runes = append(runes, 'д')
case 0xE5: // е
runes = append(runes, 'е')
case 0xE6: // ж
runes = append(runes, 'ж')
case 0xE7: // з
runes = append(runes, 'з')
case 0xE8: // и
runes = append(runes, 'и')
case 0xE9: // й
runes = append(runes, 'й')
case 0xEA: // к
runes = append(runes, 'к')
case 0xEB: // л
runes = append(runes, 'л')
case 0xEC: // м
runes = append(runes, 'м')
case 0xED: // н
runes = append(runes, 'н')
case 0xEE: // о
runes = append(runes, 'о')
case 0xEF: // п
runes = append(runes, 'п')
case 0xF0: // р
runes = append(runes, 'р')
case 0xF1: // с
runes = append(runes, 'с')
case 0xF2: // т
runes = append(runes, 'т')
case 0xF3: // у
runes = append(runes, 'у')
case 0xF4: // ф
runes = append(runes, 'ф')
case 0xF5: // х
runes = append(runes, 'х')
case 0xF6: // ц
runes = append(runes, 'ц')
case 0xF7: // ч
runes = append(runes, 'ч')
case 0xF8: // ш
runes = append(runes, 'ш')
case 0xF9: // щ
runes = append(runes, 'щ')
case 0xFA: // ъ
runes = append(runes, 'ъ')
case 0xFB: // ы
runes = append(runes, 'ы')
case 0xFC: // ь
runes = append(runes, 'ь')
case 0xFD: // э
runes = append(runes, 'э')
case 0xFE: // ю
runes = append(runes, 'ю')
case 0xFF: // я
runes = append(runes, 'я')
default:
runes = append(runes, rune(b))
}
}
}
return string(runes)
}

322
benadis_rac_helpers.go Normal file
View File

@@ -0,0 +1,322 @@
package benadis_rac
import (
"context"
"fmt"
"regexp"
"strings"
)
// getClusterUUID получает UUID кластера
func (c *Client) getClusterUUID(ctx context.Context) (string, error) {
c.logger.Debug("Getting cluster UUID")
args := []string{
c.config.RACPath,
"cluster", "list",
c.getRACAddress(),
}
output, err := c.executeCommand(ctx, args)
if err != nil {
return "", fmt.Errorf("failed to get cluster list: %w", err)
}
// Ищем UUID кластера в выводе
uuidRegex := regexp.MustCompile(`cluster\s*:\s*([a-fA-F0-9-]{36})`)
matches := uuidRegex.FindStringSubmatch(output)
if len(matches) < 2 {
return "", fmt.Errorf("cluster UUID not found in output")
}
clusterUUID := matches[1]
c.logger.Debug("Found cluster UUID", "uuid", clusterUUID)
return clusterUUID, nil
}
// getInfobaseUUID получает UUID информационной базы
func (c *Client) getInfobaseUUID(ctx context.Context, clusterUUID string) (string, error) {
c.logger.Debug("Getting infobase UUID", "cluster", clusterUUID)
args := []string{
c.config.RACPath,
"infobase", "summary", "list",
"--cluster=" + clusterUUID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
c.getRACAddress(),
}
output, err := c.executeCommand(ctx, args)
if err != nil {
return "", fmt.Errorf("failed to get infobase list: %w", err)
}
// Ищем информационную базу по имени
lines := strings.Split(output, "\n")
var currentInfobase string
var currentUUID string
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "infobase") {
// Извлекаем UUID из строки вида "infobase : uuid"
parts := strings.Split(line, ":")
if len(parts) >= 2 {
currentUUID = strings.TrimSpace(parts[1])
}
} else if strings.HasPrefix(line, "name") {
// Извлекаем имя из строки вида "name : basename"
parts := strings.Split(line, ":")
if len(parts) >= 2 {
currentInfobase = strings.TrimSpace(parts[1])
if currentInfobase == c.config.BaseName && currentUUID != "" {
c.logger.Debug("Found infobase UUID", "uuid", currentUUID, "name", currentInfobase)
return currentUUID, nil
}
}
}
}
return "", fmt.Errorf("infobase UUID not found for name: %s", c.config.BaseName)
}
// setServiceMode устанавливает сервисный режим
func (c *Client) setServiceMode(ctx context.Context, clusterUUID, infobaseUUID string, enable bool) error {
var action string
if enable {
c.logger.Info("Enabling service mode")
action = "on"
} else {
c.logger.Info("Disabling service mode")
action = "off"
}
deniedMessage := c.config.DeniedMessage
if deniedMessage == "" {
deniedMessage = DefaultDeniedMessage
}
permissionCode := c.config.PermissionCode
if permissionCode == "" {
permissionCode = DefaultPermissionCode
}
args := []string{
c.config.RACPath,
"infobase", "update",
"--cluster=" + clusterUUID,
"--infobase=" + infobaseUUID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
"--infobase-user=" + c.config.DBAdmin,
"--infobase-pwd=" + c.config.DBAdminPassword,
"--sessions-deny=" + action,
"--scheduled-jobs-deny=" + action,
c.getRACAddress(),
}
if enable {
args = append(args, "--denied-message="+deniedMessage)
args = append(args, "--permission-code="+permissionCode)
}
_, err := c.executeCommand(ctx, args)
if err != nil {
if enable {
return fmt.Errorf("failed to enable service mode: %w", err)
}
return fmt.Errorf("failed to disable service mode: %w", err)
}
return nil
}
// terminateAllSessions завершает все активные сессии
func (c *Client) terminateAllSessions(ctx context.Context, clusterUUID, infobaseUUID string) error {
c.logger.Info("Terminating all sessions")
// Получаем список сессий
sessions, err := c.getSessions(ctx, clusterUUID, infobaseUUID)
if err != nil {
return fmt.Errorf("failed to get sessions: %w", err)
}
if len(sessions) == 0 {
c.logger.Info("No active sessions found")
return nil
}
c.logger.Info("Found active sessions", "count", len(sessions))
// Завершаем каждую сессию
for _, sessionID := range sessions {
if err := c.terminateSession(ctx, clusterUUID, sessionID); err != nil {
c.logger.Warn("Failed to terminate session", "session", sessionID, "error", err)
continue
}
c.logger.Debug("Session terminated", "session", sessionID)
}
c.logger.Info("All sessions termination completed")
return nil
}
// getSessions получает список активных сессий
func (c *Client) getSessions(ctx context.Context, clusterUUID, infobaseUUID string) ([]string, error) {
args := []string{
c.config.RACPath,
"session", "list",
"--cluster=" + clusterUUID,
"--infobase=" + infobaseUUID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
c.getRACAddress(),
}
output, err := c.executeCommand(ctx, args)
if err != nil {
return nil, fmt.Errorf("failed to get session list: %w", err)
}
// Парсим вывод для извлечения ID сессий
var sessions []string
lines := strings.Split(output, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "session") {
// Извлекаем UUID из строки вида "session : uuid"
parts := strings.Split(line, ":")
if len(parts) >= 2 {
sessionID := strings.TrimSpace(parts[1])
if isValidUUID(sessionID) {
sessions = append(sessions, sessionID)
}
}
}
}
return sessions, nil
}
// terminateSession завершает конкретную сессию
func (c *Client) terminateSession(ctx context.Context, clusterUUID, sessionID string) error {
args := []string{
c.config.RACPath,
"session", "terminate",
"--cluster=" + clusterUUID,
"--session=" + sessionID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
"--message=" + TechnicalMaintenanceMessage,
c.getRACAddress(),
}
_, err := c.executeCommand(ctx, args)
if err != nil {
return fmt.Errorf("failed to terminate session %s: %w", sessionID, err)
}
return nil
}
// verifyServiceMode проверяет статус сервисного режима
func (c *Client) verifyServiceMode(ctx context.Context, clusterUUID, infobaseUUID string, expectedEnabled bool) error {
c.logger.Debug("Verifying service mode", "expected_enabled", expectedEnabled)
args := []string{
c.config.RACPath,
"infobase", "info",
"--cluster=" + clusterUUID,
"--infobase=" + infobaseUUID,
"--cluster-user=" + c.config.ClusterAdmin,
"--cluster-pwd=" + c.config.ClusterAdminPassword,
"--infobase-user=" + c.config.DBAdmin,
"--infobase-pwd=" + c.config.DBAdminPassword,
c.getRACAddress(),
}
output, err := c.executeCommand(ctx, args)
if err != nil {
return fmt.Errorf("failed to get infobase info: %w", err)
}
// Проверяем статус sessions-deny
expectedSessionsDeny := "off"
if expectedEnabled {
expectedSessionsDeny = "on"
}
if !strings.Contains(output, "sessions-deny") || !strings.Contains(output, expectedSessionsDeny) {
return fmt.Errorf("sessions-deny verification failed: expected %s, got output: %s", expectedSessionsDeny, output)
}
// Проверяем статус scheduled-jobs-deny
expectedJobsDeny := "off"
if expectedEnabled {
expectedJobsDeny = "on"
}
if !strings.Contains(output, "scheduled-jobs-deny") || !strings.Contains(output, expectedJobsDeny) {
return fmt.Errorf("scheduled-jobs-deny verification failed: expected %s, got output: %s", expectedJobsDeny, output)
}
c.logger.Debug("Service mode verification successful")
return nil
}
// isValidUUID проверяет, является ли строка валидным UUID
func isValidUUID(uuid string) bool {
if len(uuid) != UUIDLength {
return false
}
dashCount := strings.Count(uuid, "-")
return dashCount == UUIDDashCount
}
// SetDefaults устанавливает значения по умолчанию для конфигурации
func (c *Config) SetDefaults() {
if c.ConnectionTimeout == 0 {
c.ConnectionTimeout = DefaultConnectionTimeout
}
if c.CommandTimeout == 0 {
c.CommandTimeout = DefaultCommandTimeout
}
if c.RetryCount == 0 {
c.RetryCount = DefaultRetryCount
}
if c.RetryDelay == 0 {
c.RetryDelay = DefaultRetryDelay
}
if c.RACPort == 0 {
c.RACPort = DefaultRACPort
}
if c.DeniedMessage == "" {
c.DeniedMessage = DefaultDeniedMessage
}
if c.PermissionCode == "" {
c.PermissionCode = DefaultPermissionCode
}
}
// Validate проверяет корректность конфигурации
func (c *Config) Validate() error {
if c.ServerHost == "" {
return fmt.Errorf("server_host is required")
}
if c.RACPath == "" {
return fmt.Errorf("rac_path is required")
}
if c.BaseName == "" {
return fmt.Errorf("base_name is required")
}
if c.ClusterAdmin == "" {
return fmt.Errorf("cluster_admin is required")
}
if c.DBAdmin == "" {
return fmt.Errorf("db_admin is required")
}
return nil
}

View File

@@ -1,266 +0,0 @@
// Package main демонстрирует использование библиотеки benadis-rac
// для управления сервисным режимом 1C
package main
import (
"context"
"fmt"
"log"
"os"
"time"
benadisrac "git.benadis.ru/gitops/benadis-rac"
)
// ExampleConfig содержит примеры конфигурации для демонстрации
type ExampleConfig struct {
ConfigPath string
SecretPath string
ProjectPath string
LogLevel string
}
// getDefaultConfig возвращает конфигурацию по умолчанию
func getDefaultConfig() ExampleConfig {
return ExampleConfig{
ConfigPath: "../config.yaml",
SecretPath: "../secret.yaml",
ProjectPath: "../project.yaml",
LogLevel: "Info",
}
}
// demonstrateBasicUsage демонстрирует базовое использование библиотеки
func demonstrateBasicUsage() error {
fmt.Println("=== Демонстрация базового использования библиотеки GitOps RAC ===")
// Получаем конфигурацию
cfg := getDefaultConfig()
// Создаем клиент
fmt.Println("Создание клиента...")
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: cfg.LogLevel,
})
if err != nil {
return fmt.Errorf("ошибка создания клиента: %w", err)
}
fmt.Println("✓ Клиент успешно создан")
// Создаем контекст с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Получаем текущий статус
fmt.Println("\nПроверка текущего статуса сервисного режима...")
status, err := client.GetServiceModeStatus(ctx)
if err != nil {
return fmt.Errorf("ошибка получения статуса: %w", err)
}
fmt.Printf("✓ Текущий статус сервисного режима: %t\n", status)
return nil
}
// demonstrateServiceModeOperations демонстрирует операции с сервисным режимом
func demonstrateServiceModeOperations() error {
fmt.Println("\n=== Демонстрация операций с сервисным режимом ===")
// Создаем клиент
cfg := getDefaultConfig()
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: "Debug", // Используем Debug для подробного логирования
})
if err != nil {
return fmt.Errorf("ошибка создания клиента: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Получаем начальный статус
initialStatus, err := client.GetServiceModeStatus(ctx)
if err != nil {
return fmt.Errorf("ошибка получения начального статуса: %w", err)
}
fmt.Printf("Начальный статус: %t\n", initialStatus)
// Если сервисный режим выключен, включаем его
if !initialStatus {
fmt.Println("\nВключение сервисного режима...")
if err := client.EnableServiceMode(ctx); err != nil {
return fmt.Errorf("ошибка включения сервисного режима: %w", err)
}
fmt.Println("✓ Сервисный режим включен")
// Проверяем статус после включения
status, err := client.GetServiceModeStatus(ctx)
if err != nil {
return fmt.Errorf("ошибка проверки статуса после включения: %w", err)
}
fmt.Printf("✓ Статус после включения: %t\n", status)
// Ждем немного
time.Sleep(2 * time.Second)
// Выключаем сервисный режим
fmt.Println("\nВыключение сервисного режима...")
if err := client.DisableServiceMode(ctx); err != nil {
return fmt.Errorf("ошибка выключения сервисного режима: %w", err)
}
fmt.Println("✓ Сервисный режим выключен")
} else {
fmt.Println("\nВыключение сервисного режима...")
if err := client.DisableServiceMode(ctx); err != nil {
return fmt.Errorf("ошибка выключения сервисного режима: %w", err)
}
fmt.Println("✓ Сервисный режим выключен")
// Проверяем статус после выключения
status, err := client.GetServiceModeStatus(ctx)
if err != nil {
return fmt.Errorf("ошибка проверки статуса после выключения: %w", err)
}
fmt.Printf("✓ Статус после выключения: %t\n", status)
// Ждем немного
time.Sleep(2 * time.Second)
// Включаем сервисный режим обратно
fmt.Println("\nВключение сервисного режима обратно...")
if err := client.EnableServiceMode(ctx); err != nil {
return fmt.Errorf("ошибка включения сервисного режима: %w", err)
}
fmt.Println("✓ Сервисный режим включен обратно")
}
// Финальная проверка статуса
finalStatus, err := client.GetServiceModeStatus(ctx)
if err != nil {
return fmt.Errorf("ошибка получения финального статуса: %w", err)
}
fmt.Printf("\n✓ Финальный статус: %t\n", finalStatus)
return nil
}
// demonstrateErrorHandling демонстрирует обработку ошибок
func demonstrateErrorHandling() {
fmt.Println("\n=== Демонстрация обработки ошибок ===")
// Попытка создать клиент с неверными путями
fmt.Println("Попытка создания клиента с неверными путями...")
_, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: "nonexistent-config.yaml",
SecretPath: "nonexistent-secret.yaml",
ProjectPath: "nonexistent-project.yaml",
LogLevel: "Info",
})
if err != nil {
fmt.Printf("✓ Ожидаемая ошибка: %v\n", err)
} else {
fmt.Println("⚠ Неожиданно: ошибка не возникла")
}
// Попытка создать клиент с неверным уровнем логирования
fmt.Println("\nПопытка создания клиента с неверным уровнем логирования...")
cfg := getDefaultConfig()
cfg.LogLevel = "InvalidLevel"
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: cfg.LogLevel,
})
if err != nil {
fmt.Printf("✓ Ошибка при неверном уровне логирования: %v\n", err)
} else if client != nil {
fmt.Println("✓ Клиент создан, но может использовать уровень логирования по умолчанию")
}
}
// printUsageInstructions выводит инструкции по использованию
func printUsageInstructions() {
fmt.Println("\n=== Инструкции по использованию библиотеки GitOps RAC ===")
fmt.Println("\n1. Импортируйте библиотеку:")
fmt.Println(` import benadisrac "git.benadis.ru/gitops/benadis-rac/"`)
fmt.Println("\n2. Создайте конфигурацию:")
fmt.Println(` cfg := benadisrac.Config{`)
fmt.Println(` ConfigPath: "config.yaml",`)
fmt.Println(` SecretPath: "secret.yaml",`)
fmt.Println(` ProjectPath: "project.yaml",`)
fmt.Println(` LogLevel: "Info",`)
fmt.Println(` }`)
fmt.Println("\n3. Создайте клиент:")
fmt.Println(` client, err := benadisrac.NewClient(cfg)`)
fmt.Println(` if err != nil {`)
fmt.Println(` log.Fatal(err)`)
fmt.Println(` }`)
fmt.Println("\n4. Используйте методы клиента:")
fmt.Println(` ctx := context.Background()`)
fmt.Println(` status, err := client.GetServiceModeStatus(ctx)`)
fmt.Println(` err = client.EnableServiceMode(ctx)`)
fmt.Println(` err = client.DisableServiceMode(ctx)`)
fmt.Println("\n5. Доступные уровни логирования: Debug, Info, Warn, Error")
fmt.Println("\n6. Все операции поддерживают context.Context для управления таймаутами")
}
func main() {
fmt.Println("GitOps RAC Library Example")
fmt.Println("==========================")
// Проверяем аргументы командной строки
if len(os.Args) > 1 {
switch os.Args[1] {
case "--help", "-h":
printUsageInstructions()
return
case "--basic":
if err := demonstrateBasicUsage(); err != nil {
log.Printf("Ошибка в базовой демонстрации: %v", err)
}
return
case "--operations":
if err := demonstrateServiceModeOperations(); err != nil {
log.Printf("Ошибка в демонстрации операций: %v", err)
}
return
case "--errors":
demonstrateErrorHandling()
return
}
}
// Выполняем все демонстрации по умолчанию
fmt.Println("Запуск всех демонстраций...")
fmt.Println("Используйте флаги: --basic, --operations, --errors, --help")
fmt.Println()
// Базовое использование
if err := demonstrateBasicUsage(); err != nil {
log.Printf("Ошибка в базовой демонстрации: %v", err)
}
// Операции с сервисным режимом (только если базовая демонстрация прошла успешно)
if err := demonstrateServiceModeOperations(); err != nil {
log.Printf("Ошибка в демонстрации операций: %v", err)
}
// Обработка ошибок
demonstrateErrorHandling()
// Инструкции
printUsageInstructions()
fmt.Println("\n=== Демонстрация завершена ===")
}

View File

@@ -1,337 +0,0 @@
package main
import (
"context"
"os"
"testing"
"time"
benadisrac "git.benadis.ru/gitops/benadis-rac"
)
// TestGetDefaultConfig тестирует функцию получения конфигурации по умолчанию
func TestGetDefaultConfig(t *testing.T) {
cfg := getDefaultConfig()
if cfg.ConfigPath == "" {
t.Error("ConfigPath не должен быть пустым")
}
if cfg.SecretPath == "" {
t.Error("SecretPath не должен быть пустым")
}
if cfg.ProjectPath == "" {
t.Error("ProjectPath не должен быть пустым")
}
if cfg.LogLevel == "" {
t.Error("LogLevel не должен быть пустым")
}
// Проверяем ожидаемые значения
expected := ExampleConfig{
ConfigPath: "../config.yaml",
SecretPath: "../secret.yaml",
ProjectPath: "../project.yaml",
LogLevel: "Info",
}
if cfg != expected {
t.Errorf("Неожиданная конфигурация. Получено: %+v, ожидалось: %+v", cfg, expected)
}
}
// TestClientCreation тестирует создание клиента с различными конфигурациями
func TestClientCreation(t *testing.T) {
tests := []struct {
name string
config benadisrac.Config
expectError bool
}{
{
name: "Валидная конфигурация",
config: benadisrac.Config{
ConfigPath: "../config.yaml",
SecretPath: "../secret.yaml",
ProjectPath: "../project.yaml",
LogLevel: "Info",
},
expectError: false,
},
{
name: "Несуществующий файл конфигурации",
config: benadisrac.Config{
ConfigPath: "nonexistent.yaml",
SecretPath: "../secret.yaml",
ProjectPath: "../project.yaml",
LogLevel: "Info",
},
expectError: true,
},
{
name: "Несуществующий файл секретов",
config: benadisrac.Config{
ConfigPath: "../config.yaml",
SecretPath: "nonexistent.yaml",
ProjectPath: "../project.yaml",
LogLevel: "Info",
},
expectError: true,
},
{
name: "Несуществующий файл проекта",
config: benadisrac.Config{
ConfigPath: "../config.yaml",
SecretPath: "../secret.yaml",
ProjectPath: "nonexistent.yaml",
LogLevel: "Info",
},
expectError: true,
},
{
name: "Пустые пути (должны использоваться значения по умолчанию)",
config: benadisrac.Config{
LogLevel: "Debug",
},
expectError: true, // Файлы по умолчанию не существуют в example директории
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, err := benadisrac.NewClient(tt.config)
if tt.expectError {
if err == nil {
t.Error("Ожидалась ошибка, но её не было")
}
if client != nil {
t.Error("Клиент не должен быть создан при ошибке")
}
} else {
if err != nil {
t.Errorf("Неожиданная ошибка: %v", err)
}
if client == nil {
t.Error("Клиент должен быть создан")
}
}
})
}
}
// TestClientOperations тестирует операции клиента (требует валидную конфигурацию)
func TestClientOperations(t *testing.T) {
// Проверяем наличие файлов конфигурации
cfg := getDefaultConfig()
if !fileExists(cfg.ConfigPath) || !fileExists(cfg.SecretPath) || !fileExists(cfg.ProjectPath) {
t.Skip("Пропускаем тест: файлы конфигурации не найдены")
}
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: "Info",
})
if err != nil {
t.Fatalf("Ошибка создания клиента: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Тестируем получение статуса
t.Run("GetServiceModeStatus", func(t *testing.T) {
status, err := client.GetServiceModeStatus(ctx)
if err != nil {
t.Errorf("Ошибка получения статуса: %v", err)
}
// Статус может быть true или false, оба варианта валидны
t.Logf("Текущий статус сервисного режима: %t", status)
})
// Тестируем включение сервисного режима
t.Run("EnableServiceMode", func(t *testing.T) {
err := client.EnableServiceMode(ctx)
if err != nil {
t.Errorf("Ошибка включения сервисного режима: %v", err)
}
})
// Тестируем выключение сервисного режима
t.Run("DisableServiceMode", func(t *testing.T) {
err := client.DisableServiceMode(ctx)
if err != nil {
t.Errorf("Ошибка выключения сервисного режима: %v", err)
}
})
}
// TestClientWithContext тестирует работу с контекстом
func TestClientWithContext(t *testing.T) {
cfg := getDefaultConfig()
if !fileExists(cfg.ConfigPath) || !fileExists(cfg.SecretPath) || !fileExists(cfg.ProjectPath) {
t.Skip("Пропускаем тест: файлы конфигурации не найдены")
}
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: "Info",
})
if err != nil {
t.Fatalf("Ошибка создания клиента: %v", err)
}
// Тестируем таймаут контекста
t.Run("ContextTimeout", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
// Ждем, чтобы контекст истек
time.Sleep(2 * time.Millisecond)
_, err := client.GetServiceModeStatus(ctx)
if err == nil {
t.Log("Операция завершилась быстрее таймаута или таймаут не обрабатывается")
} else {
t.Logf("Ожидаемая ошибка таймаута: %v", err)
}
})
// Тестируем отмену контекста
t.Run("ContextCancellation", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // Отменяем контекст сразу
_, err := client.GetServiceModeStatus(ctx)
if err == nil {
t.Log("Операция завершилась до проверки отмены или отмена не обрабатывается")
} else {
t.Logf("Ожидаемая ошибка отмены: %v", err)
}
})
}
// TestDemonstrateBasicUsage тестирует функцию демонстрации базового использования
func TestDemonstrateBasicUsage(t *testing.T) {
cfg := getDefaultConfig()
if !fileExists(cfg.ConfigPath) || !fileExists(cfg.SecretPath) || !fileExists(cfg.ProjectPath) {
t.Skip("Пропускаем тест: файлы конфигурации не найдены")
}
err := demonstrateBasicUsage()
if err != nil {
t.Errorf("Ошибка в демонстрации базового использования: %v", err)
}
}
// TestDemonstrateErrorHandling тестирует функцию демонстрации обработки ошибок
func TestDemonstrateErrorHandling(t *testing.T) {
// Эта функция не должна вызывать панику
defer func() {
if r := recover(); r != nil {
t.Errorf("Функция demonstrateErrorHandling вызвала панику: %v", r)
}
}()
demonstrateErrorHandling()
}
// TestMainWithArguments тестирует обработку аргументов командной строки
func TestMainWithArguments(t *testing.T) {
tests := []struct {
name string
args []string
}{
{"Help", []string{"program", "--help"}},
{"Help short", []string{"program", "-h"}},
{"Errors", []string{"program", "--errors"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Сохраняем оригинальные аргументы
origArgs := os.Args
defer func() { os.Args = origArgs }()
// Устанавливаем тестовые аргументы
os.Args = tt.args
// Проверяем, что функции не вызывают панику
defer func() {
if r := recover(); r != nil {
t.Errorf("Функция вызвала панику с аргументами %v: %v", tt.args, r)
}
}()
// Тестируем отдельные функции вместо main()
switch tt.name {
case "Help", "Help short":
printUsageInstructions()
case "Errors":
demonstrateErrorHandling()
}
})
}
}
// Вспомогательные функции
// fileExists проверяет существование файла
func fileExists(filename string) bool {
_, err := os.Stat(filename)
return !os.IsNotExist(err)
}
// BenchmarkClientCreation бенчмарк создания клиента
func BenchmarkClientCreation(b *testing.B) {
cfg := getDefaultConfig()
if !fileExists(cfg.ConfigPath) || !fileExists(cfg.SecretPath) || !fileExists(cfg.ProjectPath) {
b.Skip("Пропускаем бенчмарк: файлы конфигурации не найдены")
}
config := benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: "Info",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
client, err := benadisrac.NewClient(config)
if err != nil {
b.Fatalf("Ошибка создания клиента: %v", err)
}
_ = client
}
}
// BenchmarkGetStatus бенчмарк получения статуса
func BenchmarkGetStatus(b *testing.B) {
cfg := getDefaultConfig()
if !fileExists(cfg.ConfigPath) || !fileExists(cfg.SecretPath) || !fileExists(cfg.ProjectPath) {
b.Skip("Пропускаем бенчмарк: файлы конфигурации не найдены")
}
client, err := benadisrac.NewClient(benadisrac.Config{
ConfigPath: cfg.ConfigPath,
SecretPath: cfg.SecretPath,
ProjectPath: cfg.ProjectPath,
LogLevel: "Info",
})
if err != nil {
b.Fatalf("Ошибка создания клиента: %v", err)
}
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := client.GetServiceModeStatus(ctx)
if err != nil {
b.Fatalf("Ошибка получения статуса: %v", err)
}
}
}

View File

@@ -0,0 +1,7 @@
module example
go 1.24.5
require git.benadis.ru/gitops/benadis-rac v0.0.0
replace git.benadis.ru/gitops/benadis-rac => ../..

View File

@@ -0,0 +1,108 @@
package main
import (
"context"
"log"
"time"
benadisrac "git.benadis.ru/gitops/benadis-rac"
)
func main() {
// Пример 1: Использование функции ManageServiceMode (рекомендуемый способ)
log.Println("=== Пример 1: Использование ManageServiceMode ===")
// Создаем конфигурацию
config := &benadisrac.Config{
// RAC конфигурация
RACPath: "C:\\Program Files\\1cv8\\8.3.25.1257\\bin\\rac.exe",
ConnectionTimeout: 30 * time.Second,
CommandTimeout: 60 * time.Second,
RetryCount: 3,
RetryDelay: 5 * time.Second,
// Настройки сервера
ServerHost: "localhost",
ServerPort: 1541,
RACPort: 1545,
ServerName: "server1c",
BaseName: "test_base",
// Аутентификация
ClusterAdmin: "admin",
ClusterAdminPassword: "password",
DBAdmin: "dbadmin",
DBAdminPassword: "dbpassword",
// Настройки режима обслуживания
DeniedMessage: "Система находится в режиме обслуживания",
PermissionCode: "MaintenanceMode",
}
// Устанавливаем значения по умолчанию
config.SetDefaults()
// Валидируем конфигурацию
if err := config.Validate(); err != nil {
log.Fatalf("Ошибка валидации конфигурации: %v", err)
}
// Создаем логгер
logger := benadisrac.NewLogger("info")
// Создаем контекст
ctx := context.Background()
// Включаем режим обслуживания
log.Println("Включение режима обслуживания...")
if err := benadisrac.ManageServiceMode(ctx, config, logger, true); err != nil {
log.Printf("Ошибка включения режима обслуживания: %v", err)
} else {
log.Println("Режим обслуживания успешно включен")
}
// Ждем немного
time.Sleep(2 * time.Second)
// Отключаем режим обслуживания
log.Println("Отключение режима обслуживания...")
if err := benadisrac.ManageServiceMode(ctx, config, logger, false); err != nil {
log.Printf("Ошибка отключения режима обслуживания: %v", err)
} else {
log.Println("Режим обслуживания успешно отключен")
}
// Пример 2: Использование Client напрямую
log.Println("\n=== Пример 2: Использование Client напрямую ===")
// Создаем клиента с той же конфигурацией
client, err := benadisrac.NewClient(config)
if err != nil {
log.Fatalf("Ошибка создания клиента: %v", err)
}
// Получаем статус режима обслуживания
log.Println("Получение статуса режима обслуживания...")
status, err := client.GetServiceModeStatus(ctx)
if err != nil {
log.Printf("Ошибка получения статуса: %v", err)
} else {
log.Printf("Текущий статус режима обслуживания: %t", status)
}
// Включаем режим обслуживания
log.Println("Включение режима обслуживания через Client...")
if err := client.EnableServiceMode(ctx); err != nil {
log.Printf("Ошибка включения режима обслуживания: %v", err)
} else {
log.Println("Режим обслуживания успешно включен")
}
// Отключаем режим обслуживания
log.Println("Отключение режима обслуживания через Client...")
if err := client.DisableServiceMode(ctx); err != nil {
log.Printf("Ошибка отключения режима обслуживания: %v", err)
} else {
log.Println("Режим обслуживания успешно отключен")
}
}