diff --git a/.travis.yml b/.travis.yml index fbe69a2..c1a8797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,7 @@ before_script: - sudo virsh define .travis/test-domain.xml - sudo virsh start test - sudo virsh pool-create .travis/test-pool.xml + - sudo virsh secret-define .travis/test-secret.xml script: - ./scripts/licensecheck.sh diff --git a/.travis/test-secret.xml b/.travis/test-secret.xml new file mode 100644 index 0000000..bfe53a3 --- /dev/null +++ b/.travis/test-secret.xml @@ -0,0 +1,7 @@ + + test + 19fdc2f2-fa64-46f3-bacf-42a8aafca6dd + + /tmp + + diff --git a/internal/constants/constants.go b/internal/constants/constants.go index fdf0f34..21fdfab 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -51,6 +51,7 @@ const ( ProcDomainDestroyFlags = 234 ProcConnectListAllDomains = 273 ProcConnectListAllStoragePools = 281 + ProcConnectListAllSecrets = 287 ProcMigratePerformParams = 305 ProcDomainDefineXMLFlags = 350 ) diff --git a/libvirt.go b/libvirt.go index b4b5ea3..1c64840 100644 --- a/libvirt.go +++ b/libvirt.go @@ -70,6 +70,13 @@ type DomainEvent struct { Details []byte } +// Secret represents a secret managed by the libvirt daemon. +type Secret struct { + UUID [constants.UUIDSize]byte + UsageType SecretUsageType + UsageID string +} + // StoragePool represents a storage pool as seen by libvirt. type StoragePool struct { Name string @@ -209,6 +216,20 @@ const ( DomainStateLast ) +// SecretUsageType specifies the usage for a libvirt secret. +type SecretUsageType uint32 + +const ( + // SecretUsageTypeNone specifies no usage. + SecretUsageTypeNone SecretUsageType = iota + // SecretUsageTypeVolume specifies a volume secret. + SecretUsageTypeVolume + // SecretUsageTypeCeph specifies secrets for ceph devices. + SecretUsageTypeCeph + // SecretUsageTypeISCSI specifies secrets for ISCSI devices. + SecretUsageTypeISCSI +) + // StoragePoolsFlags specifies storage pools to list. type StoragePoolsFlags uint32 @@ -572,6 +593,45 @@ func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) { return bytes.TrimRight(data[4:], "\x00"), nil } +// Secrets returns all secrets managed by the libvirt daemon. +func (l *Libvirt) Secrets() ([]Secret, error) { + req := struct { + NeedResults uint32 + Flags uint32 + }{ + NeedResults: 1, + Flags: 0, // unused per libvirt source, callers should pass 0 + } + + buf, err := encode(&req) + if err != nil { + return nil, err + } + + resp, err := l.request(constants.ProcConnectListAllSecrets, constants.ProgramRemote, &buf) + if err != nil { + return nil, err + } + + r := <-resp + if r.Status != StatusOK { + return nil, decodeError(r.Payload) + } + + result := struct { + Secrets []Secret + Count uint32 + }{} + + dec := xdr.NewDecoder(bytes.NewReader(r.Payload)) + _, err = dec.Decode(&result) + if err != nil { + return nil, err + } + + return result.Secrets, nil +} + // StoragePool returns the storage pool associated with the provided name. // An error is returned if the requested storage pool is not found. func (l *Libvirt) StoragePool(name string) (*StoragePool, error) { diff --git a/libvirt_integration_test.go b/libvirt_integration_test.go index 14f5ba7..8e82456 100644 --- a/libvirt_integration_test.go +++ b/libvirt_integration_test.go @@ -21,6 +21,8 @@ import ( "net" "testing" "time" + + "github.com/digitalocean/go-libvirt/internal/constants" ) const testAddr = "127.0.0.1:16509" @@ -70,6 +72,47 @@ func TestCapabilities(t *testing.T) { } } +func TestSecretsIntegration(t *testing.T) { + l := New(testConn(t)) + defer l.Disconnect() + + if err := l.Connect(); err != nil { + t.Fatal(err) + } + + secrets, err := l.Secrets() + if err != nil { + t.Fatal(err) + } + + wantLen := 1 + gotLen := len(secrets) + if gotLen != wantLen { + t.Fatal("expected %d secrets, got %d", wantLen, gotLen) + } + + s := secrets[0] + + wantType := SecretUsageTypeVolume + if s.UsageType != wantType { + t.Error("expected usage type: %d, got %d", wantType, s.UsageType) + } + + wantID := "/tmp" + if s.UsageID != wantID { + t.Error("expected usage id: %q, got %q", wantID, s.UsageID) + } + + // 19fdc2f2-fa64-46f3-bacf-42a8aafca6dd + wantUUID := [constants.UUIDSize]byte{ + 0x19, 0xfd, 0xc2, 0xf2, 0xfa, 0x64, 0x46, 0xf3, + 0xba, 0xcf, 0x42, 0xa8, 0xaa, 0xfc, 0xa6, 0xdd, + } + if s.UUID != wantUUID { + t.Errorf("expected UUID %q, got %q", wantUUID, s.UUID) + } +} + func TestStoragePoolIntegration(t *testing.T) { l := New(testConn(t)) defer l.Disconnect() diff --git a/libvirt_test.go b/libvirt_test.go index f23a132..6ad9543 100644 --- a/libvirt_test.go +++ b/libvirt_test.go @@ -221,6 +221,42 @@ func TestRunFail(t *testing.T) { } } +func TestSecrets(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + secrets, err := l.Secrets() + if err != nil { + t.Fatal(err) + } + + wantLen := 1 + gotLen := len(secrets) + if gotLen != wantLen { + t.Fatalf("expected %d secrets, got %d", wantLen, gotLen) + } + + s := secrets[0] + wantType := SecretUsageTypeVolume + if s.UsageType != wantType { + t.Errorf("expected usage type %d, got %d", wantType, s.UsageType) + } + + wantID := "/tmp" + if s.UsageID != wantID { + t.Errorf("expected usage id %q, got %q", wantID, s.UsageID) + } + + // 19fdc2f2-fa64-46f3-bacf-42a8aafca6dd + wantUUID := [constants.UUIDSize]byte{ + 0x19, 0xfd, 0xc2, 0xf2, 0xfa, 0x64, 0x46, 0xf3, + 0xba, 0xcf, 0x42, 0xa8, 0xaa, 0xfc, 0xa6, 0xdd, + } + if s.UUID != wantUUID { + t.Errorf("expected UUID %q, got %q", wantUUID, s.UUID) + } +} + func TestStoragePool(t *testing.T) { conn := libvirttest.New() l := New(conn) diff --git a/libvirttest/libvirt.go b/libvirttest/libvirt.go index 80bf7d1..779cffe 100644 --- a/libvirttest/libvirt.go +++ b/libvirttest/libvirt.go @@ -217,6 +217,33 @@ var testDomainStateReply = []byte{ 0x00, 0x00, 0x00, 0x01, // reason } +var testSecretsReply = []byte{ + 0x00, 0x00, 0x00, 0x40, // length + 0x20, 0x00, 0x80, 0x86, // program + 0x00, 0x00, 0x00, 0x01, // version + 0x00, 0x00, 0x01, 0x1f, // procedure + 0x00, 0x00, 0x00, 0x01, // type + 0x00, 0x00, 0x00, 0x00, // serial + 0x00, 0x00, 0x00, 0x00, // status + + // list of secrets + 0x00, 0x00, 0x00, 0x01, + + // first secret + // UUID: 19fdc2f2fa64-46f3bacf42a8aafca6dd + 0x19, 0xfd, 0xc2, 0xf2, 0xfa, 0x64, 0x46, 0xf3, + 0xba, 0xcf, 0x42, 0xa8, 0xaa, 0xfc, 0xa6, 0xdd, + + // usage type: (1, volume) + 0x00, 0x00, 0x00, 0x01, + + // usage id: "/tmp" + 0x00, 0x00, 0x00, 0x04, 0x2f, 0x74, 0x6d, 0x70, + + // end of secrets + 0x00, 0x00, 0x00, 0x01, +} + var testStoragePoolLookup = []byte{ 0x00, 0x00, 0x00, 0x38, // length 0x20, 0x00, 0x80, 0x86, // program @@ -377,6 +404,8 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { conn.Write(m.reply(testDomainsReply)) case constants.ProcConnectListAllStoragePools: conn.Write(m.reply(testListPoolsReply)) + case constants.ProcConnectListAllSecrets: + conn.Write(m.reply(testSecretsReply)) case constants.ProcDomainGetState: conn.Write(m.reply(testDomainStateReply)) case constants.ProcDomainMigrateSetMaxSpeed: