Use upstream high-level SCADA provider

This commit is contained in:
Jeff Mitchell 2016-06-01 16:35:34 -04:00
parent b43f900766
commit ebf7ea1d75
4 changed files with 319 additions and 34 deletions

View File

@ -9,6 +9,7 @@ import (
"os/signal"
"path/filepath"
"regexp"
"strconv"
"strings"
"syscall"
"time"
@ -21,7 +22,7 @@ import (
"github.com/hashicorp/go-reap"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
scada "github.com/hashicorp/scada-client"
scada "github.com/hashicorp/scada-client/scada"
"github.com/mitchellh/cli"
)
@ -1011,9 +1012,25 @@ func (c *Command) setupScadaConn(config *Config) error {
return nil
}
scadaConfig := &scada.Config{
Service: "consul",
Version: fmt.Sprintf("%s%s", config.Version, config.VersionPrerelease),
ResourceType: "infrastructures",
Meta: map[string]string{
"auto-join": strconv.FormatBool(config.AtlasJoin),
"datacenter": config.Datacenter,
"server": strconv.FormatBool(config.Server),
},
Atlas: scada.AtlasConfig{
Endpoint: config.AtlasEndpoint,
Infrastructure: config.AtlasInfrastructure,
Token: config.AtlasToken,
},
}
// Create the new provider and listener
c.Ui.Output("Connecting to Atlas: " + config.AtlasInfrastructure)
provider, list, err := NewProvider(config, c.logOutput)
provider, list, err := scada.NewHTTPProvider(scadaConfig, c.logOutput)
if err != nil {
return err
}

238
vendor/github.com/hashicorp/scada-client/scada/scada.go generated vendored Normal file
View File

@ -0,0 +1,238 @@
package scada
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"os"
"sync"
"time"
sc "github.com/hashicorp/scada-client"
)
const (
InfrastructureResource = "infrastructures"
BoxesResource = "boxes"
SharesResource = "shares"
NomadClusterResource = "nomad-cluster"
)
// Provider wraps scada-client.Provider to allow most applications to only pull
// in this package
type Provider struct {
*sc.Provider
}
type AtlasConfig struct {
// Endpoint is the SCADA endpoint used for Atlas integration. If empty, the
// defaults from the provider are used.
Endpoint string `mapstructure:"endpoint"`
// The name of the infrastructure we belong to, e.g. "hashicorp/prod"
Infrastructure string `mapstructure:"infrastructure"`
// The Atlas authentication token
Token string `mapstructure:"token" json:"-"`
}
// Config holds the high-level information used to instantiate a SCADA provider
// and listener
type Config struct {
// The service name to use
Service string
// The version of the service
Version string
// The type of resource we represent
ResourceType string
// Metadata to send to along with the service information
Meta map[string]string
// If set, TLS certificate verification will be skipped. The value of the
// SCADA_INSECURE environment variable will be considered if this is false.
// If using SCADA_INSECURE, any non-empty value will trigger insecure mode.
Insecure bool
// Holds Atlas configuration
Atlas AtlasConfig
}
// ProviderService returns the service information for the provider
func providerService(c *Config) *sc.ProviderService {
ret := &sc.ProviderService{
Service: c.Service,
ServiceVersion: c.Version,
Capabilities: map[string]int{},
Meta: c.Meta,
ResourceType: c.ResourceType,
}
return ret
}
// providerConfig returns the configuration for the SCADA provider
func providerConfig(c *Config) *sc.ProviderConfig {
ret := &sc.ProviderConfig{
Service: providerService(c),
Handlers: map[string]sc.CapabilityProvider{},
Endpoint: c.Atlas.Endpoint,
ResourceGroup: c.Atlas.Infrastructure,
Token: c.Atlas.Token,
}
return ret
}
// NewProvider creates a new SCADA provider using the given configuration.
// Requests for the HTTP capability are passed off to the listener that is
// returned.
func NewHTTPProvider(c *Config, logOutput io.Writer) (*Provider, net.Listener, error) {
// Get the configuration of the provider
config := providerConfig(c)
config.LogOutput = logOutput
// Set the HTTP capability
config.Service.Capabilities["http"] = 1
// SCADA_INSECURE env variable is used for testing to disable TLS
// certificate verification.
insecure := c.Insecure
if !insecure {
if os.Getenv("SCADA_INSECURE") != "" {
insecure = true
}
}
if insecure {
config.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
// Create an HTTP listener and handler
list := newScadaListener(c.Atlas.Infrastructure)
config.Handlers["http"] = func(capability string, meta map[string]string,
conn io.ReadWriteCloser) error {
return list.PushRWC(conn)
}
// Create the provider
provider, err := sc.NewProvider(config)
if err != nil {
list.Close()
return nil, nil, err
}
return &Provider{provider}, list, nil
}
// scadaListener is used to return a net.Listener for
// incoming SCADA connections
type scadaListener struct {
addr *scadaAddr
pending chan net.Conn
closed bool
closedCh chan struct{}
l sync.Mutex
}
// newScadaListener returns a new listener
func newScadaListener(infra string) *scadaListener {
l := &scadaListener{
addr: &scadaAddr{infra},
pending: make(chan net.Conn),
closedCh: make(chan struct{}),
}
return l
}
// PushRWC is used to push a io.ReadWriteCloser as a net.Conn
func (s *scadaListener) PushRWC(conn io.ReadWriteCloser) error {
// Check if this already implements net.Conn
if nc, ok := conn.(net.Conn); ok {
return s.Push(nc)
}
// Wrap to implement the interface
wrapped := &scadaRWC{conn, s.addr}
return s.Push(wrapped)
}
// Push is used to add a connection to the queu
func (s *scadaListener) Push(conn net.Conn) error {
select {
case s.pending <- conn:
return nil
case <-time.After(time.Second):
return fmt.Errorf("accept timed out")
case <-s.closedCh:
return fmt.Errorf("scada listener closed")
}
}
func (s *scadaListener) Accept() (net.Conn, error) {
select {
case conn := <-s.pending:
return conn, nil
case <-s.closedCh:
return nil, fmt.Errorf("scada listener closed")
}
}
func (s *scadaListener) Close() error {
s.l.Lock()
defer s.l.Unlock()
if s.closed {
return nil
}
s.closed = true
close(s.closedCh)
return nil
}
func (s *scadaListener) Addr() net.Addr {
return s.addr
}
// scadaAddr is used to return a net.Addr for SCADA
type scadaAddr struct {
infra string
}
func (s *scadaAddr) Network() string {
return "SCADA"
}
func (s *scadaAddr) String() string {
return fmt.Sprintf("SCADA::Atlas::%s", s.infra)
}
type scadaRWC struct {
io.ReadWriteCloser
addr *scadaAddr
}
func (s *scadaRWC) LocalAddr() net.Addr {
return s.addr
}
func (s *scadaRWC) RemoteAddr() net.Addr {
return s.addr
}
func (s *scadaRWC) SetDeadline(t time.Time) error {
return errors.New("SCADA.Conn does not support deadlines")
}
func (s *scadaRWC) SetReadDeadline(t time.Time) error {
return errors.New("SCADA.Conn does not support deadlines")
}
func (s *scadaRWC) SetWriteDeadline(t time.Time) error {
return errors.New("SCADA.Conn does not support deadlines")
}

View File

@ -1,4 +1,4 @@
package agent
package scada
import (
"net"
@ -9,25 +9,33 @@ import (
)
func TestProviderService(t *testing.T) {
conf := DefaultConfig()
conf.Version = "0.5.0"
conf.VersionPrerelease = "rc1"
conf.AtlasJoin = true
conf.Server = true
ps := ProviderService(conf)
expect := &client.ProviderService{
Service: "consul",
ServiceVersion: "0.5.0rc1",
Capabilities: map[string]int{
"http": 1,
},
conf := &Config{
Version: "0.5.0rc1",
Service: "nomad",
ResourceType: NomadClusterResource,
Meta: map[string]string{
"auto-join": "true",
"region": "global",
"datacenter": "dc1",
"client": "false",
"server": "true",
},
ResourceType: "infrastructures",
}
ps := providerService(conf)
expect := &client.ProviderService{
Service: "nomad",
ServiceVersion: "0.5.0rc1",
Capabilities: map[string]int{},
Meta: map[string]string{
"auto-join": "true",
"region": "global",
"datacenter": "dc1",
"client": "false",
"server": "true",
},
ResourceType: "nomad-cluster",
}
if !reflect.DeepEqual(ps, expect) {
@ -36,33 +44,42 @@ func TestProviderService(t *testing.T) {
}
func TestProviderConfig(t *testing.T) {
conf := DefaultConfig()
conf.Version = "0.5.0"
conf.VersionPrerelease = "rc1"
conf.AtlasJoin = true
conf.Server = true
conf.AtlasInfrastructure = "armon/test"
conf.AtlasToken = "foobarbaz"
conf.AtlasEndpoint = "foo.bar:1111"
pc := ProviderConfig(conf)
conf := &Config{
Version: "0.5.0rc1",
Service: "nomad",
ResourceType: NomadClusterResource,
Meta: map[string]string{
"auto-join": "true",
"region": "global",
"datacenter": "dc1",
"client": "false",
"server": "true",
},
}
conf.Atlas = AtlasConfig{
Infrastructure: "armon/test",
Token: "foobarbaz",
Endpoint: "foo.bar:1111",
}
pc := providerConfig(conf)
expect := &client.ProviderConfig{
Service: &client.ProviderService{
Service: "consul",
Service: "nomad",
ServiceVersion: "0.5.0rc1",
Capabilities: map[string]int{
"http": 1,
},
Capabilities: map[string]int{},
Meta: map[string]string{
"auto-join": "true",
"region": "global",
"datacenter": "dc1",
"client": "false",
"server": "true",
},
ResourceType: "infrastructures",
},
Handlers: map[string]client.CapabilityProvider{
"http": nil,
ResourceType: "nomad-cluster",
},
Handlers: map[string]client.CapabilityProvider{},
Endpoint: "foo.bar:1111",
ResourceGroup: "armon/test",
Token: "foobarbaz",

13
vendor/vendor.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"comment": "",
"ignore": "",
"package": [
{
"checksumSHA1": "Vrd+ucpioTcZGglPx2JWUxb2J7A=",
"path": "github.com/hashicorp/scada-client/scada",
"revision": "225f7ae4780b59d1bd990de93cae7496bade95a0",
"revisionTime": "2016-06-01T18:41:54Z"
}
],
"rootPath": "github.com/hashicorp/consul"
}