From d0b4eb4d690dff7768b5ae588450fc202d286a6a Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 18 Sep 2014 21:56:39 +0200 Subject: [PATCH 1/1] Initial commit for the github.com/sysdb/go Go packages. This is the initial version of core Go bindings for SysDB (https://sysdb.io/). For now, two packages are provided: - github.com/sysdb/go/client: A SysDB client implementation. - github.com/sysdb/go/proto: Helper functions for using the SysDB front-end protocol. That's the protocol used for communication between a client and a SysDB server instance. --- COPYING | 24 ++++++++ README | 70 +++++++++++++++++++++++ client/client.go | 141 +++++++++++++++++++++++++++++++++++++++++++++++ proto/proto.go | 132 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+) create mode 100644 COPYING create mode 100644 README create mode 100644 client/client.go create mode 100644 proto/proto.go diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b7a1f1b --- /dev/null +++ b/COPYING @@ -0,0 +1,24 @@ +Copyright (c) 2014 Sebastian 'tokkee' Harl +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README b/README new file mode 100644 index 0000000..0dbe76d --- /dev/null +++ b/README @@ -0,0 +1,70 @@ + Core Go bindings for SysDB + ============================ + + The core Go bindings for SysDB are a set of packages for the Go programming + language providing core functionality to interact with SysDB. + +What is SysDB? +-------------- + + “System DataBase” (SysDB) is a multi-backend system management and inventory + collection service. It stores system and inventory information about + hardware and software systems. This information is (continuously) collected + from various configurable backends (inventory services, monitoring services, + etc.) and stored in a graph-like hierarchy of generic objects. The store may + be queried through a generic interface independent of the active backends. + Object names are canonicalized before they are added to the store to ensure + a consistent view of your infrastructure. + + The central object type is a host, which generally represents a physical or + virtual machine or any other type of physical resource. Hosts, in turn, may + reference a list of services which represent any kind of logical resource + like a software system. Both, hosts and services, may reference a list of + attributes which represent further information about the respective host or + service object. For example, attributes may specify static information like + a host's architecture or the software version. A host may also reference a + list of metrics which are references to performance data stored about the + host. SysDB supports querying the respective time-series from a backend's + data store. + + SysDB is free and open source software, licensed under the 2-clause BSD + license. See COPYING for details. Changes between all SysDB releases can be + found in the file ReleaseNotes. + + + +Install the Go bindings +----------------------- + + Installing all of the packages provided by this project is as easy as + running the following command: + + go get github.com/sysdb/go/... + + This will download and install the packages and all of their dependencies + into GOPATH. See ‘go help get’ for more details. See below for a list of all + packages and their descriptions. + +Packages +-------- + + * github.com/sysdb/go/client: A SysDB client implementation. + + * github.com/sysdb/go/proto: Helper functions for using the SysDB front-end + protocol. That's the protocol used for communication between a client and + a SysDB server instance. + +Getting Help +------------ + + Various channels for asynchronous and real-time communication with + developers and users are available. See for + details about the mailing list, IRC channel, and social media. + +Author +------ + + Sebastian “tokkee” Harl + + Want to contribute? Check out the website for details. + diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..131fdd5 --- /dev/null +++ b/client/client.go @@ -0,0 +1,141 @@ +// +// Copyright (C) 2014 Sebastian 'tokkee' Harl +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package client provides a SysDB client implementation. + +The Connect function connects to a SysDB server as the specified user: + + c, err := client.Connect("unix:/var/run/sysdbd.sock", "username") + if err != nil { + // handle error + } + defer c.Close() + +The github.com/sysdb/go/proto package provides support for handling requests +and responses. Use the Send and Receive functions to communicate with the +server: + + m := &proto.Message{ + Type: proto.ConnectionQuery, + Raw: []byte{"LOOKUP hosts MATCHING attribute.architecture = 'amd64';"}, + } + if err := c.Send(m); err != nil { + // handle error + } + m, err := c.Receive() + if err != nil { + // handle error + } + if m.Type == proto.ConnectionError { + // handle failed query + } + // ... +*/ +package client + +import ( + "fmt" + "net" + "strings" + + "github.com/sysdb/go/proto" +) + +// A Conn is a connection to a SysDB server instance. +// +// Multiple goroutines may invoke methods on a Conn simultaneously but since +// the SysDB protocol requires a strict ordering of request and response +// messages, the communication with the server will usually happen +// sequentially. +type Conn struct { + c net.Conn +} + +// Connect sets up a client connection to a SysDB server instance at the +// specified address using the specified user. +// +// The address may be a UNIX domain socket, either prefixed with 'unix:' or +// specifying an absolute file-system path. +func Connect(addr, user string) (*Conn, error) { + network := "tcp" + if strings.HasPrefix(addr, "unix:") { + network = "unix" + addr = addr[len("unix:"):] + } else if addr[0] == '/' { + network = "unix" + } + + c := &Conn{} + var err error + if c.c, err = net.Dial(network, addr); err != nil { + return nil, err + } + + m := &proto.Message{ + Type: proto.ConnectionStartup, + Raw: []byte(user), + } + if err := c.Send(m); err != nil { + return nil, err + } + + m, err = c.Receive() + if err != nil { + return nil, err + } + if m.Type == proto.ConnectionError { + return nil, fmt.Errorf("failed to startup session: %s", string(m.Raw)) + } + if m.Type != proto.ConnectionOK { + return nil, fmt.Errorf("failed to startup session: unsupported") + } + return c, nil +} + +// Close closes the client connection. +// +// Any blocked Send or Receive operations will be unblocked and return errors. +func (c Conn) Close() { c.c.Close() } + +// Send encodes the specified message and sends it to the server. +// +// Send operations block until the full message could be written to the +// underlying sockets. This ensures that server and client don't get out of +// sync. +func (c Conn) Send(m *proto.Message) error { + return proto.Encode(c.c, m) +} + +// Receive waits for a reply from the server and returns the decoded message. +// +// Receive operations block until a full message could be read from the +// underlying socket. This ensures that server and client don't get out of +// sync. +func (c Conn) Receive() (*proto.Message, error) { + return proto.Decode(c.c) +} + +// vim: set tw=78 sw=4 sw=4 noexpandtab : diff --git a/proto/proto.go b/proto/proto.go new file mode 100644 index 0000000..5aadf9a --- /dev/null +++ b/proto/proto.go @@ -0,0 +1,132 @@ +// +// Copyright (C) 2014 Sebastian 'tokkee' Harl +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package proto provides helper functions for using the SysDB front-end +// protocol. That's the protocol used for communication between a client and a +// SysDB server instance. +package proto + +import ( + "encoding/binary" + "io" +) + +// Network byte order. +var nbo = binary.BigEndian + +// A Status represents the type of a message. The message type describes the +// current status or state of a connection depending on the context. +type Status uint32 + +const ( + // ConnectionOK indicates that a command was successful. + ConnectionOK = Status(0) + // ConnectionError indicates that a command has failed. + ConnectionError = Status(1) + // ConnectionLog indicates an (asynchronous) log message. + ConnectionLog = Status(2) + + // ConnectionData indicates a successful query returning data. + ConnectionData = Status(100) +) + +const ( + // ConnectionIdle is the internal state for idle connections. + ConnectionIdle = Status(0) + // ConnectionPing is the state requesting a connection check. + ConnectionPing = Status(1) + // ConnectionStartup is the state requesting the setup of a client + // connection. + ConnectionStartup = Status(2) + + // ConnectionQuery is the state requesting the execution of a query in the + // server. + ConnectionQuery = Status(3) + // ConnectionFetch is the state requesting the execution of the 'FETCH' + // command in the server. + ConnectionFetch = Status(4) + // ConnectionList is the state requesting the execution of the 'LIST' + // command in the server. + ConnectionList = Status(5) + // ConnectionLookup is the state requesting the execution of the 'LOOKUP' + // command in the server. + ConnectionLookup = Status(6) + // ConnectionTimeseries is the state requesting the execution of the + // 'TIMESERIES' command in the server. + ConnectionTimeseries = Status(7) + + // ConnectionExpr is the internal state for expression parsing. + ConnectionExpr = Status(100) +) + +// A Message represents a raw message of the SysDB front-end protocol. +type Message struct { + Type Status + Raw []byte +} + +// Decodes reads a raw message encoded in the SysDB wire format from r. The +// raw body of the message will still be encoded in the wire format. +// +// The reader has to be in blocking mode. Otherwise, the client and server +// will be out of sync after reading a partial message and cannot recover from +// that. +func Decode(r io.Reader) (*Message, error) { + var header [8]byte + if _, err := io.ReadFull(r, header[:]); err != nil { + return nil, err + } + + typ := nbo.Uint32(header[:4]) + l := nbo.Uint32(header[4:]) + msg := make([]byte, l) + if _, err := io.ReadFull(r, msg); err != nil { + return nil, err + } + + return &Message{Status(typ), msg}, nil +} + +// Encode writes a raw message to w. The raw body of m has to be encoded in +// the SysDB wire format. +// +// The writer has to be in blocking mode. Otherwise, the client and server +// will be out of sync after writing a partial message and cannot recover from +// that. +func Encode(w io.Writer, m *Message) error { + var header [8]byte + nbo.PutUint32(header[:4], uint32(m.Type)) + nbo.PutUint32(header[4:], uint32(len(m.Raw))) + + if _, err := io.WriteString(w, string(header[:])); err != nil { + return err + } + if _, err := io.WriteString(w, string(m.Raw)); err != nil { + return err + } + return nil +} + +// vim: set tw=78 sw=4 sw=4 noexpandtab : -- 2.30.2