Cut a new release. Simplify Debian build rules, more similar to govpp-snmp-agentx
This commit is contained in:
1
Makefile
1
Makefile
@ -29,6 +29,7 @@ build: sync-version
|
|||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@echo "Cleaning build artifacts..."
|
@echo "Cleaning build artifacts..."
|
||||||
|
[ -d debian/go ] && chmod -R +w debian/go || true
|
||||||
rm -rf debian/.debhelper debian/.gocache debian/go debian/$(BINARY_NAME) debian/files debian/*.substvars debian/debhelper-build-stamp
|
rm -rf debian/.debhelper debian/.gocache debian/go debian/$(BINARY_NAME) debian/files debian/*.substvars debian/debhelper-build-stamp
|
||||||
rm -f ../$(BINARY_NAME)_*.deb ../$(BINARY_NAME)_*.changes ../$(BINARY_NAME)_*.buildinfo
|
rm -f ../$(BINARY_NAME)_*.deb ../$(BINARY_NAME)_*.changes ../$(BINARY_NAME)_*.buildinfo
|
||||||
rm -f $(BUILD_DIR)/$(BINARY_NAME)
|
rm -f $(BUILD_DIR)/$(BINARY_NAME)
|
||||||
|
10
debian/changelog
vendored
10
debian/changelog
vendored
@ -1,3 +1,13 @@
|
|||||||
|
ipng-router-backup (1.1.0) stable; urgency=low
|
||||||
|
|
||||||
|
* Replace --config flag with --yaml flag supporting multiple files
|
||||||
|
* Switch from !include to mergo-based configuration merging
|
||||||
|
* Add SSH config integration for legacy device compatibility
|
||||||
|
* Refactor SSH functionality into separate module
|
||||||
|
* Add comprehensive test coverage
|
||||||
|
|
||||||
|
-- Pim van Pelt <pim@ipng.ch> Sun, 07 Jul 2025 15:30:00 +0100
|
||||||
|
|
||||||
ipng-router-backup (1.0.2) stable; urgency=low
|
ipng-router-backup (1.0.2) stable; urgency=low
|
||||||
|
|
||||||
* Add YAML !include directive support for configuration files
|
* Add YAML !include directive support for configuration files
|
||||||
|
12
debian/rules
vendored
12
debian/rules
vendored
@ -1,23 +1,31 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export GO111MODULE = on
|
||||||
|
export GOPROXY = https://proxy.golang.org,direct
|
||||||
|
export GOCACHE = $(CURDIR)/debian/.gocache
|
||||||
|
export GOPATH = $(CURDIR)/debian/go
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
override_dh_auto_build:
|
override_dh_auto_build:
|
||||||
cd src && go build -o ../ipng-router-backup main.go
|
mkdir -p $(GOCACHE) $(GOPATH)
|
||||||
|
cd src && go build -o ../ipng-router-backup .
|
||||||
|
|
||||||
override_dh_auto_install:
|
override_dh_auto_install:
|
||||||
mkdir -p debian/ipng-router-backup/usr/bin
|
mkdir -p debian/ipng-router-backup/usr/bin
|
||||||
mkdir -p debian/ipng-router-backup/etc/ipng-router-backup
|
mkdir -p debian/ipng-router-backup/etc/ipng-router-backup
|
||||||
mkdir -p debian/ipng-router-backup/usr/share/man/man1
|
mkdir -p debian/ipng-router-backup/usr/share/man/man1
|
||||||
cp ipng-router-backup debian/ipng-router-backup/usr/bin/
|
cp ipng-router-backup debian/ipng-router-backup/usr/bin/
|
||||||
cp docs/config.yaml.example debian/ipng-router-backup/etc/ipng-router-backup/config.yaml.example
|
|
||||||
cp etc/* debian/ipng-router-backup/etc/ipng-router-backup/
|
cp etc/* debian/ipng-router-backup/etc/ipng-router-backup/
|
||||||
cp docs/router_backup.1 debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
|
cp docs/router_backup.1 debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
|
||||||
gzip debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
|
gzip debian/ipng-router-backup/usr/share/man/man1/ipng-router-backup.1
|
||||||
|
|
||||||
override_dh_auto_clean:
|
override_dh_auto_clean:
|
||||||
rm -f ipng-router-backup
|
rm -f ipng-router-backup
|
||||||
|
[ -d debian/go ] && chmod -R +w debian/go || true
|
||||||
|
for dir in obj-*; do [ -d "$$dir" ] && chmod -R +w "$$dir" || true; done
|
||||||
|
rm -rf debian/.gocache debian/go obj-*
|
||||||
|
|
||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
# Skip tests for now
|
# Skip tests for now
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Version = "1.0.2"
|
const Version = "1.1.0"
|
||||||
|
|
||||||
// Config and SSH types are now in separate packages
|
// Config and SSH types are now in separate packages
|
||||||
|
|
||||||
|
@ -8,27 +8,27 @@ import (
|
|||||||
|
|
||||||
func TestNewRouterBackup(t *testing.T) {
|
func TestNewRouterBackup(t *testing.T) {
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "/path/to/key", 2222)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "/path/to/key", 2222)
|
||||||
|
|
||||||
if rb.hostname != "testhost" {
|
if rb.hostname != "testhost" {
|
||||||
t.Errorf("Expected hostname 'testhost', got '%s'", rb.hostname)
|
t.Errorf("Expected hostname 'testhost', got '%s'", rb.hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rb.username != "testuser" {
|
if rb.username != "testuser" {
|
||||||
t.Errorf("Expected username 'testuser', got '%s'", rb.username)
|
t.Errorf("Expected username 'testuser', got '%s'", rb.username)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rb.password != "testpass" {
|
if rb.password != "testpass" {
|
||||||
t.Errorf("Expected password 'testpass', got '%s'", rb.password)
|
t.Errorf("Expected password 'testpass', got '%s'", rb.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rb.keyFile != "/path/to/key" {
|
if rb.keyFile != "/path/to/key" {
|
||||||
t.Errorf("Expected keyFile '/path/to/key', got '%s'", rb.keyFile)
|
t.Errorf("Expected keyFile '/path/to/key', got '%s'", rb.keyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rb.port != 2222 {
|
if rb.port != 2222 {
|
||||||
t.Errorf("Expected port 2222, got %d", rb.port)
|
t.Errorf("Expected port 2222, got %d", rb.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rb.client != nil {
|
if rb.client != nil {
|
||||||
t.Error("Expected client to be nil initially")
|
t.Error("Expected client to be nil initially")
|
||||||
}
|
}
|
||||||
@ -36,12 +36,12 @@ func TestNewRouterBackup(t *testing.T) {
|
|||||||
|
|
||||||
func TestRunCommandWithoutConnection(t *testing.T) {
|
func TestRunCommandWithoutConnection(t *testing.T) {
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
_, err := rb.RunCommand("show version")
|
_, err := rb.RunCommand("show version")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error when running command without connection")
|
t.Error("Expected error when running command without connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err.Error() != "no active connection" {
|
if err.Error() != "no active connection" {
|
||||||
t.Errorf("Expected 'no active connection' error, got '%s'", err.Error())
|
t.Errorf("Expected 'no active connection' error, got '%s'", err.Error())
|
||||||
}
|
}
|
||||||
@ -50,18 +50,18 @@ func TestRunCommandWithoutConnection(t *testing.T) {
|
|||||||
func TestBackupCommandsDirectoryCreation(t *testing.T) {
|
func TestBackupCommandsDirectoryCreation(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
outputDir := filepath.Join(tempDir, "nonexistent", "backup")
|
outputDir := filepath.Join(tempDir, "nonexistent", "backup")
|
||||||
|
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
// This should create the directory even without a connection
|
// This should create the directory even without a connection
|
||||||
// and fail gracefully when trying to run commands
|
// and fail gracefully when trying to run commands
|
||||||
_ = rb.BackupCommands([]string{"show version"}, outputDir)
|
_ = rb.BackupCommands([]string{"show version"}, outputDir)
|
||||||
|
|
||||||
// Should not error on directory creation
|
// Should not error on directory creation
|
||||||
if _, statErr := os.Stat(outputDir); os.IsNotExist(statErr) {
|
if _, statErr := os.Stat(outputDir); os.IsNotExist(statErr) {
|
||||||
t.Error("Expected output directory to be created")
|
t.Error("Expected output directory to be created")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should create the output file even if commands fail
|
// Should create the output file even if commands fail
|
||||||
expectedFile := filepath.Join(outputDir, "testhost")
|
expectedFile := filepath.Join(outputDir, "testhost")
|
||||||
if _, statErr := os.Stat(expectedFile); os.IsNotExist(statErr) {
|
if _, statErr := os.Stat(expectedFile); os.IsNotExist(statErr) {
|
||||||
@ -71,14 +71,14 @@ func TestBackupCommandsDirectoryCreation(t *testing.T) {
|
|||||||
|
|
||||||
func TestBackupCommandsEmptyCommands(t *testing.T) {
|
func TestBackupCommandsEmptyCommands(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
err := rb.BackupCommands([]string{}, tempDir)
|
err := rb.BackupCommands([]string{}, tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error for empty commands list, got %v", err)
|
t.Errorf("Expected no error for empty commands list, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should still create the output file
|
// Should still create the output file
|
||||||
expectedFile := filepath.Join(tempDir, "testhost")
|
expectedFile := filepath.Join(tempDir, "testhost")
|
||||||
if _, statErr := os.Stat(expectedFile); os.IsNotExist(statErr) {
|
if _, statErr := os.Stat(expectedFile); os.IsNotExist(statErr) {
|
||||||
@ -88,7 +88,7 @@ func TestBackupCommandsEmptyCommands(t *testing.T) {
|
|||||||
|
|
||||||
func TestDisconnectWithoutConnection(t *testing.T) {
|
func TestDisconnectWithoutConnection(t *testing.T) {
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
// Should not panic or error when disconnecting without connection
|
// Should not panic or error when disconnecting without connection
|
||||||
rb.Disconnect()
|
rb.Disconnect()
|
||||||
}
|
}
|
||||||
@ -99,31 +99,31 @@ func TestFindDefaultSSHKey(t *testing.T) {
|
|||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
os.Setenv("HOME", tempDir)
|
os.Setenv("HOME", tempDir)
|
||||||
defer os.Setenv("HOME", originalHome)
|
defer os.Setenv("HOME", originalHome)
|
||||||
|
|
||||||
keyPath := findDefaultSSHKey()
|
keyPath := findDefaultSSHKey()
|
||||||
if keyPath != "" {
|
if keyPath != "" {
|
||||||
t.Errorf("Expected empty string when no SSH keys exist, got '%s'", keyPath)
|
t.Errorf("Expected empty string when no SSH keys exist, got '%s'", keyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create .ssh directory and a test key
|
// Create .ssh directory and a test key
|
||||||
sshDir := filepath.Join(tempDir, ".ssh")
|
sshDir := filepath.Join(tempDir, ".ssh")
|
||||||
err := os.MkdirAll(sshDir, 0700)
|
err := os.MkdirAll(sshDir, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create .ssh directory: %v", err)
|
t.Fatalf("Failed to create .ssh directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create id_rsa key (should be found first)
|
// Create id_rsa key (should be found first)
|
||||||
rsaKeyPath := filepath.Join(sshDir, "id_rsa")
|
rsaKeyPath := filepath.Join(sshDir, "id_rsa")
|
||||||
err = os.WriteFile(rsaKeyPath, []byte("fake rsa key"), 0600)
|
err = os.WriteFile(rsaKeyPath, []byte("fake rsa key"), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create RSA key: %v", err)
|
t.Fatalf("Failed to create RSA key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPath = findDefaultSSHKey()
|
keyPath = findDefaultSSHKey()
|
||||||
if keyPath != rsaKeyPath {
|
if keyPath != rsaKeyPath {
|
||||||
t.Errorf("Expected to find RSA key at '%s', got '%s'", rsaKeyPath, keyPath)
|
t.Errorf("Expected to find RSA key at '%s', got '%s'", rsaKeyPath, keyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove RSA key and create ed25519 key
|
// Remove RSA key and create ed25519 key
|
||||||
os.Remove(rsaKeyPath)
|
os.Remove(rsaKeyPath)
|
||||||
ed25519KeyPath := filepath.Join(sshDir, "id_ed25519")
|
ed25519KeyPath := filepath.Join(sshDir, "id_ed25519")
|
||||||
@ -131,7 +131,7 @@ func TestFindDefaultSSHKey(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create ed25519 key: %v", err)
|
t.Fatalf("Failed to create ed25519 key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPath = findDefaultSSHKey()
|
keyPath = findDefaultSSHKey()
|
||||||
if keyPath != ed25519KeyPath {
|
if keyPath != ed25519KeyPath {
|
||||||
t.Errorf("Expected to find ed25519 key at '%s', got '%s'", ed25519KeyPath, keyPath)
|
t.Errorf("Expected to find ed25519 key at '%s', got '%s'", ed25519KeyPath, keyPath)
|
||||||
@ -143,7 +143,7 @@ func TestFindDefaultSSHKeyHomeError(t *testing.T) {
|
|||||||
originalHome := os.Getenv("HOME")
|
originalHome := os.Getenv("HOME")
|
||||||
os.Unsetenv("HOME")
|
os.Unsetenv("HOME")
|
||||||
defer os.Setenv("HOME", originalHome)
|
defer os.Setenv("HOME", originalHome)
|
||||||
|
|
||||||
keyPath := findDefaultSSHKey()
|
keyPath := findDefaultSSHKey()
|
||||||
if keyPath != "" {
|
if keyPath != "" {
|
||||||
t.Errorf("Expected empty string when HOME is not set, got '%s'", keyPath)
|
t.Errorf("Expected empty string when HOME is not set, got '%s'", keyPath)
|
||||||
@ -152,39 +152,39 @@ func TestFindDefaultSSHKeyHomeError(t *testing.T) {
|
|||||||
|
|
||||||
func TestBackupCommandsFileOperations(t *testing.T) {
|
func TestBackupCommandsFileOperations(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
// Create some fake commands (they will fail but we can test file operations)
|
// Create some fake commands (they will fail but we can test file operations)
|
||||||
commands := []string{"show version", "show interfaces"}
|
commands := []string{"show version", "show interfaces"}
|
||||||
|
|
||||||
err := rb.BackupCommands(commands, tempDir)
|
err := rb.BackupCommands(commands, tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that output file was created
|
// Check that output file was created
|
||||||
outputFile := filepath.Join(tempDir, "testhost")
|
outputFile := filepath.Join(tempDir, "testhost")
|
||||||
_, err = os.ReadFile(outputFile)
|
_, err = os.ReadFile(outputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to read output file: %v", err)
|
t.Fatalf("Failed to read output file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// File should be created (it will be empty if all commands fail)
|
// File should be created (it will be empty if all commands fail)
|
||||||
// This test just verifies the file creation works
|
// This test just verifies the file creation works
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterBackupConnectionState(t *testing.T) {
|
func TestRouterBackupConnectionState(t *testing.T) {
|
||||||
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
rb := NewRouterBackup("testhost", "testuser", "testpass", "", 22)
|
||||||
|
|
||||||
// Initially no client
|
// Initially no client
|
||||||
if rb.client != nil {
|
if rb.client != nil {
|
||||||
t.Error("Expected client to be nil initially")
|
t.Error("Expected client to be nil initially")
|
||||||
}
|
}
|
||||||
|
|
||||||
// After disconnect, should still be nil (safe to call multiple times)
|
// After disconnect, should still be nil (safe to call multiple times)
|
||||||
rb.Disconnect()
|
rb.Disconnect()
|
||||||
if rb.client != nil {
|
if rb.client != nil {
|
||||||
t.Error("Expected client to remain nil after disconnect")
|
t.Error("Expected client to remain nil after disconnect")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user