<commit_before>package drivers

import (
	"fmt"
	"path"
	"sync"

	"github.com/livepeer/go-livepeer/net"
)

var dataCacheLen = 12

type MemoryOS struct {
	baseURI  string
	sessions map[string]*MemorySession
	lock     sync.RWMutex
}

type MemorySession struct {
	os     *MemoryOS
	path   string
	ended  bool
	dCache map[string]*dataCache
	dLock  sync.RWMutex
}

func NewMemoryDriver(baseURI string) *MemoryOS {
	return &MemoryOS{
		baseURI:  baseURI,
		sessions: make(map[string]*MemorySession),
		lock:     sync.RWMutex{},
	}
}

func (ostore *MemoryOS) NewSession(path string) OSSession {
	ostore.lock.Lock()
	defer ostore.lock.Unlock()
	if session, ok := ostore.sessions[path]; ok {
		return session
	}
	session := &MemorySession{
		os:     ostore,
		path:   path + "/",
		dCache: make(map[string]*dataCache),
		dLock:  sync.RWMutex{},
	}
	ostore.sessions[path] = session
	return session
}

func (ostore *MemoryOS) GetSession(path string) *MemorySession {
	ostore.lock.Lock()
	defer ostore.lock.Unlock()
	if session, ok := ostore.sessions[path]; ok {
		return session
	}
	return nil
}

// EndSession clears memory cache
func (ostore *MemorySession) EndSession() {
	ostore.dLock.Lock()
	ostore.ended = true
	for k := range ostore.dCache {
		delete(ostore.dCache, k)
	}
	ostore.dLock.Unlock()

	ostore.os.lock.Lock()
	delete(ostore.os.sessions, ostore.path)
	ostore.os.lock.Unlock()
}

func (ostore *MemorySession) GetData(name string) []byte {

	path, file := path.Split(name)

	ostore.dLock.RLock()
	defer ostore.dLock.RUnlock()

	if cache, ok := ostore.dCache[path]; ok {
		return cache.GetData(file)
	}
	return nil
}

func (ostore *MemorySession) IsExternal() bool {
	return false
}

func (ostore *MemorySession) GetInfo() *net.OSInfo {
	return nil
}

func (ostore *MemorySession) SaveData(name string, data []byte) (string, error) {

	path, file := path.Split(ostore.path + name)

	ostore.dLock.Lock()
	defer ostore.dLock.Unlock()

	if ostore.ended {
		return "", fmt.Errorf("Session ended")
	}

	dc := ostore.getCacheForStream(path)
	dc.Insert(file, data)

	return ostore.getAbsoluteURI(name), nil
}

func (ostore *MemorySession) getCacheForStream(streamID string) *dataCache {
	sc, ok := ostore.dCache[streamID]
	if !ok {
		sc = newDataCache(dataCacheLen)
		ostore.dCache[streamID] = sc
	}
	return sc
}

func (ostore *MemorySession) getAbsoluteURI(name string) string {
	name = ostore.path + name
	if ostore.os.baseURI != "" {
		return ostore.os.baseURI + "/stream/" + name
	}
	return name
}

type dataCache struct {
	cacheLen int
	cache    []*dataCacheItem
}

type dataCacheItem struct {
	name string
	data []byte
}

func newDataCache(len int) *dataCache {
	return &dataCache{cacheLen: len, cache: make([]*dataCacheItem, 0)}
}

func (dc *dataCache) Insert(name string, data []byte) {
	// replace existing item
	for _, item := range dc.cache {
		if item.name == name {
			item.data = data
			return
		}
	}
	if len(dc.cache) >= dc.cacheLen {
		dc.cache = dc.cache[1:]
	}
	item := &dataCacheItem{name: name, data: data}
	dc.cache = append(dc.cache, item)
}

func (dc *dataCache) GetData(name string) []byte {
	for _, s := range dc.cache {
		if s.name == name {
			return s.data
		}
	}
	return nil
}
<commit_msg>memorystore: Properly handle paths and slashes.<commit_after>package drivers

import (
	"fmt"
	"path"
	"sync"

	"github.com/livepeer/go-livepeer/net"
)

var dataCacheLen = 12

type MemoryOS struct {
	baseURI  string
	sessions map[string]*MemorySession
	lock     sync.RWMutex
}

type MemorySession struct {
	os     *MemoryOS
	path   string
	ended  bool
	dCache map[string]*dataCache
	dLock  sync.RWMutex
}

func NewMemoryDriver(baseURI string) *MemoryOS {
	return &MemoryOS{
		baseURI:  baseURI,
		sessions: make(map[string]*MemorySession),
		lock:     sync.RWMutex{},
	}
}

func (ostore *MemoryOS) NewSession(path string) OSSession {
	ostore.lock.Lock()
	defer ostore.lock.Unlock()
	if session, ok := ostore.sessions[path]; ok {
		return session
	}
	session := &MemorySession{
		os:     ostore,
		path:   path,
		dCache: make(map[string]*dataCache),
		dLock:  sync.RWMutex{},
	}
	ostore.sessions[path] = session
	return session
}

func (ostore *MemoryOS) GetSession(path string) *MemorySession {
	ostore.lock.Lock()
	defer ostore.lock.Unlock()
	if session, ok := ostore.sessions[path]; ok {
		return session
	}
	return nil
}

// EndSession clears memory cache
func (ostore *MemorySession) EndSession() {
	ostore.dLock.Lock()
	ostore.ended = true
	for k := range ostore.dCache {
		delete(ostore.dCache, k)
	}
	ostore.dLock.Unlock()

	ostore.os.lock.Lock()
	delete(ostore.os.sessions, ostore.path)
	ostore.os.lock.Unlock()
}

func (ostore *MemorySession) GetData(name string) []byte {

	path, file := path.Split(name)

	ostore.dLock.RLock()
	defer ostore.dLock.RUnlock()

	if cache, ok := ostore.dCache[path]; ok {
		return cache.GetData(file)
	}
	return nil
}

func (ostore *MemorySession) IsExternal() bool {
	return false
}

func (ostore *MemorySession) GetInfo() *net.OSInfo {
	return nil
}

func (ostore *MemorySession) SaveData(name string, data []byte) (string, error) {

	path, file := path.Split(ostore.getAbsolutePath(name))

	ostore.dLock.Lock()
	defer ostore.dLock.Unlock()

	if ostore.ended {
		return "", fmt.Errorf("Session ended")
	}

	dc := ostore.getCacheForStream(path)
	dc.Insert(file, data)

	return ostore.getAbsoluteURI(name), nil
}

func (ostore *MemorySession) getCacheForStream(streamID string) *dataCache {
	sc, ok := ostore.dCache[streamID]
	if !ok {
		sc = newDataCache(dataCacheLen)
		ostore.dCache[streamID] = sc
	}
	return sc
}

func (ostore *MemorySession) getAbsolutePath(name string) string {
	return path.Clean(ostore.path + "/" + name)
}

func (ostore *MemorySession) getAbsoluteURI(name string) string {
	name = ostore.getAbsolutePath(name)
	if ostore.os.baseURI != "" {
		return ostore.os.baseURI + "/stream/" + name
	}
	return name
}

type dataCache struct {
	cacheLen int
	cache    []*dataCacheItem
}

type dataCacheItem struct {
	name string
	data []byte
}

func newDataCache(len int) *dataCache {
	return &dataCache{cacheLen: len, cache: make([]*dataCacheItem, 0)}
}

func (dc *dataCache) Insert(name string, data []byte) {
	// replace existing item
	for _, item := range dc.cache {
		if item.name == name {
			item.data = data
			return
		}
	}
	if len(dc.cache) >= dc.cacheLen {
		dc.cache = dc.cache[1:]
	}
	item := &dataCacheItem{name: name, data: data}
	dc.cache = append(dc.cache, item)
}

func (dc *dataCache) GetData(name string) []byte {
	for _, s := range dc.cache {
		if s.name == name {
			return s.data
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package selenium

import (
	"time"

	"github.com/tebeka/selenium/chrome"
	"github.com/tebeka/selenium/firefox"
	"github.com/tebeka/selenium/log"
)

// TODO(minusnine): make an enum type called FindMethod.

// Methods by which to find elements.
const (
	ByID              = "id"
	ByXPATH           = "xpath"
	ByLinkText        = "link text"
	ByPartialLinkText = "partial link text"
	ByName            = "name"
	ByTagName         = "tag name"
	ByClassName       = "class name"
	ByCSSSelector     = "css selector"
)

// Mouse buttons.
const (
	LeftButton = iota
	MiddleButton
	RightButton
)

// Special keyboard keys, for SendKeys.
const (
	NullKey       = string('\ue000')
	CancelKey     = string('\ue001')
	HelpKey       = string('\ue002')
	BackspaceKey  = string('\ue003')
	TabKey        = string('\ue004')
	ClearKey      = string('\ue005')
	ReturnKey     = string('\ue006')
	EnterKey      = string('\ue007')
	ShiftKey      = string('\ue008')
	ControlKey    = string('\ue009')
	AltKey        = string('\ue00a')
	PauseKey      = string('\ue00b')
	EscapeKey     = string('\ue00c')
	SpaceKey      = string('\ue00d')
	PageUpKey     = string('\ue00e')
	PageDownKey   = string('\ue00f')
	EndKey        = string('\ue010')
	HomeKey       = string('\ue011')
	LeftArrowKey  = string('\ue012')
	UpArrowKey    = string('\ue013')
	RightArrowKey = string('\ue014')
	DownArrowKey  = string('\ue015')
	InsertKey     = string('\ue016')
	DeleteKey     = string('\ue017')
	SemicolonKey  = string('\ue018')
	EqualsKey     = string('\ue019')
	Numpad0Key    = string('\ue01a')
	Numpad1Key    = string('\ue01b')
	Numpad2Key    = string('\ue01c')
	Numpad3Key    = string('\ue01d')
	Numpad4Key    = string('\ue01e')
	Numpad5Key    = string('\ue01f')
	Numpad6Key    = string('\ue020')
	Numpad7Key    = string('\ue021')
	Numpad8Key    = string('\ue022')
	Numpad9Key    = string('\ue023')
	MultiplyKey   = string('\ue024')
	AddKey        = string('\ue025')
	SeparatorKey  = string('\ue026')
	SubstractKey  = string('\ue027')
	DecimalKey    = string('\ue028')
	DivideKey     = string('\ue029')
	F1Key         = string('\ue031')
	F2Key         = string('\ue032')
	F3Key         = string('\ue033')
	F4Key         = string('\ue034')
	F5Key         = string('\ue035')
	F6Key         = string('\ue036')
	F7Key         = string('\ue037')
	F8Key         = string('\ue038')
	F9Key         = string('\ue039')
	F10Key        = string('\ue03a')
	F11Key        = string('\ue03b')
	F12Key        = string('\ue03c')
	MetaKey       = string('\ue03d')
)

// Capabilities configures both the WebDriver process and the target browsers,
// with standard and browser-specific options.
type Capabilities map[string]interface{}

// AddChrome adds Chrome-specific capabilities.
func (c Capabilities) AddChrome(f chrome.Capabilities) {
	c[chrome.CapabilitiesKey] = f
}

// AddFirefox adds Firefox-specific capabilities.
func (c Capabilities) AddFirefox(f firefox.Capabilities) {
	c[firefox.CapabilitiesKey] = f
}

// AddProxy adds proxy configuration to the capabilities.
func (c Capabilities) AddProxy(p Proxy) {
	c["proxy"] = p
}

// AddLogging adds logging configuration to the capabilities.
func (c Capabilities) AddLogging(l log.Capabilities) {
	c[log.CapabilitiesKey] = l
}

// SetLogLevel sets the logging level of a component. It is a shortcut for
// passing a log.Capabilities instance to AddLogging.
func (c Capabilities) SetLogLevel(typ log.Type, level log.Level) {
	if _, ok := c[log.CapabilitiesKey]; !ok {
		c[log.CapabilitiesKey] = make(log.Capabilities)
	}
	m := c[log.CapabilitiesKey].(log.Capabilities)
	m[typ] = level
}

// Proxy specifies configuration for proxies in the browser. Set the key
// "proxy" in Capabilities to an instance of this type.
type Proxy struct {
	// Type is the type of proxy to use. This is required to be populated.
	Type ProxyType `json:"proxyType"`

	// AutoconfigURL is the URL to be used for proxy auto configuration. This is
	// required if Type is set to PAC.
	AutoconfigURL string `json:"proxyAutoconfigUrl,omitempty"`

	// The following are used when Type is set to Manual.
	//
	// Note that in Firefox, connections to localhost are not proxied by default,
	// even if a proxy is set. This can be overrided via a preference setting.
	FTP           string `json:"ftpProxy,omitempty"`
	HTTP          string `json:"httpProxy,omitempty"`
	SSL           string `json:"sslProxy,omitempty"`
	SOCKS         string `json:"socksProxy,omitempty"`
	SOCKSUsername string `json:"socksUsername,omitempty"`
	SOCKSPassword string `json:"socksPassword,omitempty"`
	NoProxy       string `json:"noProxy,omitempty"`

	// The W3C draft spec includes port fields as well. According to the
	// specification, ports can also be included in the above addresses. However,
	// in the Geckodriver implementation, the ports must be specified by these
	// additional fields.
	HTTPPort  int `json:"httpProxyPort,omitempty"`
	SSLPort   int `json:"sslProxyPort,omitempty"`
	SocksPort int `json:"socksProxyPort,omitempty"`
}

// ProxyType is an enumeration of the types of proxies available.
type ProxyType string

const (
	// Direct connection - no proxy in use.
	Direct ProxyType = "direct"
	// Manual proxy settings configured, e.g. setting a proxy for HTTP, a proxy
	// for FTP, etc.
	Manual = "manual"
	// Autodetect proxy, probably with WPAD
	Autodetect = "autodetect"
	// System settings used.
	System = "system"
	// PAC - Proxy autoconfiguration from a URL.
	PAC = "pac"
)

// Status contains information returned by the Status method.
type Status struct {
	// The following fields are used by Selenium and ChromeDriver.
	Java struct {
		Version string
	}
	Build struct {
		Version, Revision, Time string
	}
	OS struct {
		Arch, Name, Version string
	}

	// The following fields are specified by the W3C WebDriver specification and
	// are used by GeckoDriver.
	Ready   bool
	Message string
}

// Point is a 2D point.
type Point struct {
	X, Y int
}

// Size is a size of HTML element.
type Size struct {
	Width, Height int
}

// Cookie represents an HTTP cookie.
type Cookie struct {
	Name   string `json:"name"`
	Value  string `json:"value"`
	Path   string `json:"path"`
	Domain string `json:"domain"`
	Secure bool   `json:"secure"`
	Expiry uint   `json:"expiry"`
}

// WebDriver defines methods supported by WebDriver drivers.
type WebDriver interface {
	// Status returns various pieces of information about the server environment.
	Status() (*Status, error)

	// NewSession starts a new session and returns the session ID.
	NewSession() (string, error)

	// SessionId returns the current session ID
	//
	// Deprecated: This identifier is not Go-style correct. Use SessionID
	// instead.
	SessionId() string

	// SessionID returns the current session ID.
	SessionID() string

	// SwitchSession switches to the given session ID.
	SwitchSession(sessionID string) error

	// Capabilities returns the current session's capabilities.
	Capabilities() (Capabilities, error)

	// SetAsyncScriptTimeout sets the amount of time that asynchronous scripts
	// are permitted to run before they are aborted. The timeout will be rounded
	// to nearest millisecond.
	SetAsyncScriptTimeout(timeout time.Duration) error
	// SetImplicitWaitTimeout sets the amount of time the driver should wait when
	// searching for elements. The timeout will be rounded to nearest millisecond.
	SetImplicitWaitTimeout(timeout time.Duration) error
	// SetPageLoadTimeout sets the amount of time the driver should wait when
	// loading a page. The timeout will be rounded to nearest millisecond.
	SetPageLoadTimeout(timeout time.Duration) error

	// Quit ends the current session. The browser instance will be closed.
	Quit() error

	// CurrentWindowHandle returns the ID of current window handle.
	CurrentWindowHandle() (string, error)
	// WindowHandles returns the IDs of current open windows.
	WindowHandles() ([]string, error)
	// CurrentURL returns the browser's current URL.
	CurrentURL() (string, error)
	// Title returns the current page's title.
	Title() (string, error)
	// PageSource returns the current page's source.
	PageSource() (string, error)
	// Close closes the current window.
	Close() error
	// SwitchFrame switches to the given frame. The frame parameter can be the
	// frame's ID as a string, its WebElement instance as returned by
	// GetElement, or nil to switch to the current top-level browsing context.
	SwitchFrame(frame interface{}) error
	// SwitchWindow switches the context to the specified window.
	SwitchWindow(name string) error
	// CloseWindow closes the specified window.
	CloseWindow(name string) error
	// MaximizeWindow maximizes a window. If the name is empty, the current
	// window will be maximized.
	MaximizeWindow(name string) error
	// ResizeWindow changes the dimensions of a window. If the name is empty, the
	// current window will be maximized.
	ResizeWindow(name string, width, height int) error

	// Get navigates the browser to the provided URL.
	Get(url string) error
	// Forward moves forward in history.
	Forward() error
	// Back moves backward in history.
	Back() error
	// Refresh refreshes the page.
	Refresh() error

	// FindElement finds exactly one element in the current page's DOM.
	FindElement(by, value string) (WebElement, error)
	// FindElement finds potentially many elements in the current page's DOM.
	FindElements(by, value string) ([]WebElement, error)
	// ActiveElement returns the currently active element on the page.
	ActiveElement() (WebElement, error)

	// DecodeElement decodes a single element response.
	DecodeElement([]byte) (WebElement, error)
	// DecodeElements decodes a multi-element response.
	DecodeElements([]byte) ([]WebElement, error)

	// GetCookies returns all of the cookies in the browser's jar.
	GetCookies() ([]Cookie, error)
	// GetCookie returns the named cookie in the jar, if present. This method is
	// only implemented for Firefox.
	GetCookie(name string) (Cookie, error)
	// AddCookie adds a cookie to the browser's jar.
	AddCookie(cookie *Cookie) error
	// DeleteAllCookies deletes all of the cookies in the browser's jar.
	DeleteAllCookies() error
	// DeleteCookie deletes a cookie to the browser's jar.
	DeleteCookie(name string) error

	// Click clicks a mouse button. The button should be one of RightButton,
	// MiddleButton or LeftButton.
	Click(button int) error
	// DoubleClick clicks the left mouse button twice.
	DoubleClick() error
	// ButtonDown causes the left mouse button to be held down.
	ButtonDown() error
	// ButtonUp causes the left mouse button to be released.
	ButtonUp() error

	// SendModifier sends the modifier key to the active element. The modifier
	// can be one of ShiftKey, ControlKey, AltKey, MetaKey.
	//
	// Deprecated: Use KeyDown or KeyUp instead.
	SendModifier(modifier string, isDown bool) error
	// KeyDown sends a sequence of keystrokes to the active element. This method
	// is similar to SendKeys but without the implicit termination. Modifiers are
	// not released at the end of each call.
	KeyDown(keys string) error
	// KeyUp indicates that a previous keystroke sent by KeyDown should be
	// released.
	KeyUp(keys string) error
	// Screenshot takes a screenshot of the browser window.
	Screenshot() ([]byte, error)
	// Log fetches the logs. Log types must be previously configured in the
	// capabilities.
	//
	// NOTE: will return an error (not implemented) on IE11 or Edge drivers.
	Log(typ log.Type) ([]log.Message, error)

	// DismissAlert dismisses current alert.
	DismissAlert() error
	// AcceptAlert accepts the current alert.
	AcceptAlert() error
	// AlertText returns the current alert text.
	AlertText() (string, error)
	// SetAlertText sets the current alert text.
	SetAlertText(text string) error

	// ExecuteScript executes a script.
	ExecuteScript(script string, args []interface{}) (interface{}, error)
	// ExecuteScriptAsync asynchronously executes a script.
	ExecuteScriptAsync(script string, args []interface{}) (interface{}, error)

	// ExecuteScriptRaw executes a script but does not perform JSON decoding.
	ExecuteScriptRaw(script string, args []interface{}) ([]byte, error)
	// ExecuteScriptAsyncRaw asynchronously executes a script but does not
	// perform JSON decoding.
	ExecuteScriptAsyncRaw(script string, args []interface{}) ([]byte, error)

	// WaitWithTimeoutAndInterval waits for the condition to evaluate to true.
	WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error

	// WaitWithTimeout works like WaitWithTimeoutAndInterval, but with default polling interval.
	WaitWithTimeout(condition Condition, timeout time.Duration) error

	//Wait works like WaitWithTimeoutAndInterval, but using the default timeout and polling interval.
	Wait(condition Condition) error
}

// WebElement defines method supported by web elements.
type WebElement interface {
	// Click clicks on the element.
	Click() error
	// SendKeys types into the element.
	SendKeys(keys string) error
	// Submit submits the button.
	Submit() error
	// Clear clears the element.
	Clear() error
	// MoveTo moves the mouse to relative coordinates from center of element, If
	// the element is not visible, it will be scrolled into view.
	MoveTo(xOffset, yOffset int) error

	// FindElement finds a child element.
	FindElement(by, value string) (WebElement, error)
	// FindElement finds multiple children elements.
	FindElements(by, value string) ([]WebElement, error)

	// TagName returns the element's name.
	TagName() (string, error)
	// Text returns the text of the element.
	Text() (string, error)
	// IsSelected returns true if element is selected.
	IsSelected() (bool, error)
	// IsEnabled returns true if the element is enabled.
	IsEnabled() (bool, error)
	// IsDisplayed returns true if the element is displayed.
	IsDisplayed() (bool, error)
	// GetAttribute returns the named attribute of the element.
	GetAttribute(name string) (string, error)
	// Location returns the element's location.
	Location() (*Point, error)
	// LocationInView returns the element's location once it has been scrolled
	// into view.
	LocationInView() (*Point, error)
	// Size returns the element's size.
	Size() (*Size, error)
	// CSSProperty returns the value of the specified CSS property of the
	// element.
	CSSProperty(name string) (string, error)
}
<commit_msg>Fix typo in Proxy documentation<commit_after>package selenium

import (
	"time"

	"github.com/tebeka/selenium/chrome"
	"github.com/tebeka/selenium/firefox"
	"github.com/tebeka/selenium/log"
)

// TODO(minusnine): make an enum type called FindMethod.

// Methods by which to find elements.
const (
	ByID              = "id"
	ByXPATH           = "xpath"
	ByLinkText        = "link text"
	ByPartialLinkText = "partial link text"
	ByName            = "name"
	ByTagName         = "tag name"
	ByClassName       = "class name"
	ByCSSSelector     = "css selector"
)

// Mouse buttons.
const (
	LeftButton = iota
	MiddleButton
	RightButton
)

// Special keyboard keys, for SendKeys.
const (
	NullKey       = string('\ue000')
	CancelKey     = string('\ue001')
	HelpKey       = string('\ue002')
	BackspaceKey  = string('\ue003')
	TabKey        = string('\ue004')
	ClearKey      = string('\ue005')
	ReturnKey     = string('\ue006')
	EnterKey      = string('\ue007')
	ShiftKey      = string('\ue008')
	ControlKey    = string('\ue009')
	AltKey        = string('\ue00a')
	PauseKey      = string('\ue00b')
	EscapeKey     = string('\ue00c')
	SpaceKey      = string('\ue00d')
	PageUpKey     = string('\ue00e')
	PageDownKey   = string('\ue00f')
	EndKey        = string('\ue010')
	HomeKey       = string('\ue011')
	LeftArrowKey  = string('\ue012')
	UpArrowKey    = string('\ue013')
	RightArrowKey = string('\ue014')
	DownArrowKey  = string('\ue015')
	InsertKey     = string('\ue016')
	DeleteKey     = string('\ue017')
	SemicolonKey  = string('\ue018')
	EqualsKey     = string('\ue019')
	Numpad0Key    = string('\ue01a')
	Numpad1Key    = string('\ue01b')
	Numpad2Key    = string('\ue01c')
	Numpad3Key    = string('\ue01d')
	Numpad4Key    = string('\ue01e')
	Numpad5Key    = string('\ue01f')
	Numpad6Key    = string('\ue020')
	Numpad7Key    = string('\ue021')
	Numpad8Key    = string('\ue022')
	Numpad9Key    = string('\ue023')
	MultiplyKey   = string('\ue024')
	AddKey        = string('\ue025')
	SeparatorKey  = string('\ue026')
	SubstractKey  = string('\ue027')
	DecimalKey    = string('\ue028')
	DivideKey     = string('\ue029')
	F1Key         = string('\ue031')
	F2Key         = string('\ue032')
	F3Key         = string('\ue033')
	F4Key         = string('\ue034')
	F5Key         = string('\ue035')
	F6Key         = string('\ue036')
	F7Key         = string('\ue037')
	F8Key         = string('\ue038')
	F9Key         = string('\ue039')
	F10Key        = string('\ue03a')
	F11Key        = string('\ue03b')
	F12Key        = string('\ue03c')
	MetaKey       = string('\ue03d')
)

// Capabilities configures both the WebDriver process and the target browsers,
// with standard and browser-specific options.
type Capabilities map[string]interface{}

// AddChrome adds Chrome-specific capabilities.
func (c Capabilities) AddChrome(f chrome.Capabilities) {
	c[chrome.CapabilitiesKey] = f
}

// AddFirefox adds Firefox-specific capabilities.
func (c Capabilities) AddFirefox(f firefox.Capabilities) {
	c[firefox.CapabilitiesKey] = f
}

// AddProxy adds proxy configuration to the capabilities.
func (c Capabilities) AddProxy(p Proxy) {
	c["proxy"] = p
}

// AddLogging adds logging configuration to the capabilities.
func (c Capabilities) AddLogging(l log.Capabilities) {
	c[log.CapabilitiesKey] = l
}

// SetLogLevel sets the logging level of a component. It is a shortcut for
// passing a log.Capabilities instance to AddLogging.
func (c Capabilities) SetLogLevel(typ log.Type, level log.Level) {
	if _, ok := c[log.CapabilitiesKey]; !ok {
		c[log.CapabilitiesKey] = make(log.Capabilities)
	}
	m := c[log.CapabilitiesKey].(log.Capabilities)
	m[typ] = level
}

// Proxy specifies configuration for proxies in the browser. Set the key
// "proxy" in Capabilities to an instance of this type.
type Proxy struct {
	// Type is the type of proxy to use. This is required to be populated.
	Type ProxyType `json:"proxyType"`

	// AutoconfigURL is the URL to be used for proxy auto configuration. This is
	// required if Type is set to PAC.
	AutoconfigURL string `json:"proxyAutoconfigUrl,omitempty"`

	// The following are used when Type is set to Manual.
	//
	// Note that in Firefox, connections to localhost are not proxied by default,
	// even if a proxy is set. This can be overridden via a preference setting.
	FTP           string `json:"ftpProxy,omitempty"`
	HTTP          string `json:"httpProxy,omitempty"`
	SSL           string `json:"sslProxy,omitempty"`
	SOCKS         string `json:"socksProxy,omitempty"`
	SOCKSUsername string `json:"socksUsername,omitempty"`
	SOCKSPassword string `json:"socksPassword,omitempty"`
	NoProxy       string `json:"noProxy,omitempty"`

	// The W3C draft spec includes port fields as well. According to the
	// specification, ports can also be included in the above addresses. However,
	// in the Geckodriver implementation, the ports must be specified by these
	// additional fields.
	HTTPPort  int `json:"httpProxyPort,omitempty"`
	SSLPort   int `json:"sslProxyPort,omitempty"`
	SocksPort int `json:"socksProxyPort,omitempty"`
}

// ProxyType is an enumeration of the types of proxies available.
type ProxyType string

const (
	// Direct connection - no proxy in use.
	Direct ProxyType = "direct"
	// Manual proxy settings configured, e.g. setting a proxy for HTTP, a proxy
	// for FTP, etc.
	Manual = "manual"
	// Autodetect proxy, probably with WPAD
	Autodetect = "autodetect"
	// System settings used.
	System = "system"
	// PAC - Proxy autoconfiguration from a URL.
	PAC = "pac"
)

// Status contains information returned by the Status method.
type Status struct {
	// The following fields are used by Selenium and ChromeDriver.
	Java struct {
		Version string
	}
	Build struct {
		Version, Revision, Time string
	}
	OS struct {
		Arch, Name, Version string
	}

	// The following fields are specified by the W3C WebDriver specification and
	// are used by GeckoDriver.
	Ready   bool
	Message string
}

// Point is a 2D point.
type Point struct {
	X, Y int
}

// Size is a size of HTML element.
type Size struct {
	Width, Height int
}

// Cookie represents an HTTP cookie.
type Cookie struct {
	Name   string `json:"name"`
	Value  string `json:"value"`
	Path   string `json:"path"`
	Domain string `json:"domain"`
	Secure bool   `json:"secure"`
	Expiry uint   `json:"expiry"`
}

// WebDriver defines methods supported by WebDriver drivers.
type WebDriver interface {
	// Status returns various pieces of information about the server environment.
	Status() (*Status, error)

	// NewSession starts a new session and returns the session ID.
	NewSession() (string, error)

	// SessionId returns the current session ID
	//
	// Deprecated: This identifier is not Go-style correct. Use SessionID
	// instead.
	SessionId() string

	// SessionID returns the current session ID.
	SessionID() string

	// SwitchSession switches to the given session ID.
	SwitchSession(sessionID string) error

	// Capabilities returns the current session's capabilities.
	Capabilities() (Capabilities, error)

	// SetAsyncScriptTimeout sets the amount of time that asynchronous scripts
	// are permitted to run before they are aborted. The timeout will be rounded
	// to nearest millisecond.
	SetAsyncScriptTimeout(timeout time.Duration) error
	// SetImplicitWaitTimeout sets the amount of time the driver should wait when
	// searching for elements. The timeout will be rounded to nearest millisecond.
	SetImplicitWaitTimeout(timeout time.Duration) error
	// SetPageLoadTimeout sets the amount of time the driver should wait when
	// loading a page. The timeout will be rounded to nearest millisecond.
	SetPageLoadTimeout(timeout time.Duration) error

	// Quit ends the current session. The browser instance will be closed.
	Quit() error

	// CurrentWindowHandle returns the ID of current window handle.
	CurrentWindowHandle() (string, error)
	// WindowHandles returns the IDs of current open windows.
	WindowHandles() ([]string, error)
	// CurrentURL returns the browser's current URL.
	CurrentURL() (string, error)
	// Title returns the current page's title.
	Title() (string, error)
	// PageSource returns the current page's source.
	PageSource() (string, error)
	// Close closes the current window.
	Close() error
	// SwitchFrame switches to the given frame. The frame parameter can be the
	// frame's ID as a string, its WebElement instance as returned by
	// GetElement, or nil to switch to the current top-level browsing context.
	SwitchFrame(frame interface{}) error
	// SwitchWindow switches the context to the specified window.
	SwitchWindow(name string) error
	// CloseWindow closes the specified window.
	CloseWindow(name string) error
	// MaximizeWindow maximizes a window. If the name is empty, the current
	// window will be maximized.
	MaximizeWindow(name string) error
	// ResizeWindow changes the dimensions of a window. If the name is empty, the
	// current window will be maximized.
	ResizeWindow(name string, width, height int) error

	// Get navigates the browser to the provided URL.
	Get(url string) error
	// Forward moves forward in history.
	Forward() error
	// Back moves backward in history.
	Back() error
	// Refresh refreshes the page.
	Refresh() error

	// FindElement finds exactly one element in the current page's DOM.
	FindElement(by, value string) (WebElement, error)
	// FindElement finds potentially many elements in the current page's DOM.
	FindElements(by, value string) ([]WebElement, error)
	// ActiveElement returns the currently active element on the page.
	ActiveElement() (WebElement, error)

	// DecodeElement decodes a single element response.
	DecodeElement([]byte) (WebElement, error)
	// DecodeElements decodes a multi-element response.
	DecodeElements([]byte) ([]WebElement, error)

	// GetCookies returns all of the cookies in the browser's jar.
	GetCookies() ([]Cookie, error)
	// GetCookie returns the named cookie in the jar, if present. This method is
	// only implemented for Firefox.
	GetCookie(name string) (Cookie, error)
	// AddCookie adds a cookie to the browser's jar.
	AddCookie(cookie *Cookie) error
	// DeleteAllCookies deletes all of the cookies in the browser's jar.
	DeleteAllCookies() error
	// DeleteCookie deletes a cookie to the browser's jar.
	DeleteCookie(name string) error

	// Click clicks a mouse button. The button should be one of RightButton,
	// MiddleButton or LeftButton.
	Click(button int) error
	// DoubleClick clicks the left mouse button twice.
	DoubleClick() error
	// ButtonDown causes the left mouse button to be held down.
	ButtonDown() error
	// ButtonUp causes the left mouse button to be released.
	ButtonUp() error

	// SendModifier sends the modifier key to the active element. The modifier
	// can be one of ShiftKey, ControlKey, AltKey, MetaKey.
	//
	// Deprecated: Use KeyDown or KeyUp instead.
	SendModifier(modifier string, isDown bool) error
	// KeyDown sends a sequence of keystrokes to the active element. This method
	// is similar to SendKeys but without the implicit termination. Modifiers are
	// not released at the end of each call.
	KeyDown(keys string) error
	// KeyUp indicates that a previous keystroke sent by KeyDown should be
	// released.
	KeyUp(keys string) error
	// Screenshot takes a screenshot of the browser window.
	Screenshot() ([]byte, error)
	// Log fetches the logs. Log types must be previously configured in the
	// capabilities.
	//
	// NOTE: will return an error (not implemented) on IE11 or Edge drivers.
	Log(typ log.Type) ([]log.Message, error)

	// DismissAlert dismisses current alert.
	DismissAlert() error
	// AcceptAlert accepts the current alert.
	AcceptAlert() error
	// AlertText returns the current alert text.
	AlertText() (string, error)
	// SetAlertText sets the current alert text.
	SetAlertText(text string) error

	// ExecuteScript executes a script.
	ExecuteScript(script string, args []interface{}) (interface{}, error)
	// ExecuteScriptAsync asynchronously executes a script.
	ExecuteScriptAsync(script string, args []interface{}) (interface{}, error)

	// ExecuteScriptRaw executes a script but does not perform JSON decoding.
	ExecuteScriptRaw(script string, args []interface{}) ([]byte, error)
	// ExecuteScriptAsyncRaw asynchronously executes a script but does not
	// perform JSON decoding.
	ExecuteScriptAsyncRaw(script string, args []interface{}) ([]byte, error)

	// WaitWithTimeoutAndInterval waits for the condition to evaluate to true.
	WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error

	// WaitWithTimeout works like WaitWithTimeoutAndInterval, but with default polling interval.
	WaitWithTimeout(condition Condition, timeout time.Duration) error

	//Wait works like WaitWithTimeoutAndInterval, but using the default timeout and polling interval.
	Wait(condition Condition) error
}

// WebElement defines method supported by web elements.
type WebElement interface {
	// Click clicks on the element.
	Click() error
	// SendKeys types into the element.
	SendKeys(keys string) error
	// Submit submits the button.
	Submit() error
	// Clear clears the element.
	Clear() error
	// MoveTo moves the mouse to relative coordinates from center of element, If
	// the element is not visible, it will be scrolled into view.
	MoveTo(xOffset, yOffset int) error

	// FindElement finds a child element.
	FindElement(by, value string) (WebElement, error)
	// FindElement finds multiple children elements.
	FindElements(by, value string) ([]WebElement, error)

	// TagName returns the element's name.
	TagName() (string, error)
	// Text returns the text of the element.
	Text() (string, error)
	// IsSelected returns true if element is selected.
	IsSelected() (bool, error)
	// IsEnabled returns true if the element is enabled.
	IsEnabled() (bool, error)
	// IsDisplayed returns true if the element is displayed.
	IsDisplayed() (bool, error)
	// GetAttribute returns the named attribute of the element.
	GetAttribute(name string) (string, error)
	// Location returns the element's location.
	Location() (*Point, error)
	// LocationInView returns the element's location once it has been scrolled
	// into view.
	LocationInView() (*Point, error)
	// Size returns the element's size.
	Size() (*Size, error)
	// CSSProperty returns the value of the specified CSS property of the
	// element.
	CSSProperty(name string) (string, error)
}
<|endoftext|>


<commit_before>package outreach

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type SequenceResponse struct {
	Data   []SequenceData
	Meta   SequenceMeta
	Links  SequenceLinks
	Errors []map[string]interface{}
}

type SequenceData struct {
	ID         int
	Type       string
	Attributes SequenceAttributes
}

type SequenceAttributes struct {
	Name    string
	Created string
	Updated string
}

type SequenceMeta struct {
	Page    SequencePage
	Results SequenceResults
}

type SequencePage struct {
	Current int
	Entries int
	Maximum int
}

type SequenceResults struct {
	Total int
}

type SequenceLinks struct {
	Next string
	Prev string
	Self string
}

type SequenceAddProspect struct {
	Data SequenceAddProspectData
}

type SequenceAddProspectData struct {
	Relationships SequenceAddProspectRelationships
}

type SequenceAddProspectRelationships struct {
	Prospects []SequenceAddProspectProspect
}

type SequenceAddProspectProspect struct {
	Data SequenceAddProspectProspectData
}

type SequenceAddProspectProspectData struct {
	ID string
}

type SequenceAddProspectResponse struct {
	Data   SequenceData
	Links  SequenceLinks
	Errors []map[string]interface{}
}

type SequenceInstance struct {
	Client *http.Client
}

func (sp *SequenceAddProspect) Read(t []byte) (int, error) {
	j, e := json.Marshal(sp)
	if e != nil {
		return 0, e
	}

	if len(t) < len(j) {
		return len(t), nil
	}

	copy(t, j)
	return len(j), io.EOF
}

func (sp *SequenceAddProspect) Close() error {
	return nil
}

func (s *SequenceInstance) AddProspect(id int, pids ...int) (SequenceAddProspectResponse, error) {
	seq := SequenceAddProspectResponse{}

	if s.Client == nil {
		return seq, errors.New("You must assign a HTTP client.")
	}

	sp := SequenceAddProspect{}

	for _, v := range pids {
		sp.Data.Relationships.Prospects =
			append(sp.Data.Relationships.Prospects, SequenceAddProspectProspect{
				Data: SequenceAddProspectProspectData{
					ID: strconv.Itoa(v),
				},
			})
	}

	req, e := http.NewRequest("PATCH", "https://api.outreach.io/1.0/sequences/"+strconv.Itoa(id), &sp)
	if e != nil {
		return seq, e
	}

	resp, e := s.Client.Do(req)
	if e != nil {
		return seq, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return seq, e
	}

	e = json.Unmarshal(body, &seq)
	if e != nil {
		return seq, e
	}

	if len(seq.Errors) > 0 {
		return seq, fmt.Errorf("Got error response: %+v\n", seq.Errors)
	}

	return seq, nil
}

func (s *SequenceInstance) Get(opts ...int) (SequenceResponse, error) {
	seq := SequenceResponse{}

	if s.Client == nil {
		return seq, errors.New("You must assign a HTTP client.")
	}

	resp, e := s.Client.Get("https://api.outreach.io/1.0/sequences")
	if e != nil {
		return seq, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return seq, e
	}

	e = json.Unmarshal(body, &seq)
	if e != nil {
		return seq, e
	}

	if len(seq.Errors) > 0 {
		return seq, fmt.Errorf("Got error response: %+v\n", seq.Errors)
	}

	return seq, nil
}
<commit_msg>Make sure keys in AddProspect JSON are lowercased<commit_after>package outreach

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type SequenceResponse struct {
	Data   []SequenceData
	Meta   SequenceMeta
	Links  SequenceLinks
	Errors []map[string]interface{}
}

type SequenceData struct {
	ID         int
	Type       string
	Attributes SequenceAttributes
}

type SequenceAttributes struct {
	Name    string
	Created string
	Updated string
}

type SequenceMeta struct {
	Page    SequencePage
	Results SequenceResults
}

type SequencePage struct {
	Current int
	Entries int
	Maximum int
}

type SequenceResults struct {
	Total int
}

type SequenceLinks struct {
	Next string
	Prev string
	Self string
}

type SequenceAddProspect struct {
	Data SequenceAddProspectData `json: "data"`
}

type SequenceAddProspectData struct {
	Relationships SequenceAddProspectRelationships `json: "relationships"`
}

type SequenceAddProspectRelationships struct {
	Prospects []SequenceAddProspectProspect `json: "prospects"`
}

type SequenceAddProspectProspect struct {
	Data SequenceAddProspectProspectData `json: "data"`
}

type SequenceAddProspectProspectData struct {
	ID string `json: "id"`
}

type SequenceAddProspectResponse struct {
	Data   SequenceData
	Links  SequenceLinks
	Errors []map[string]interface{}
}

type SequenceInstance struct {
	Client *http.Client
}

func (sp *SequenceAddProspect) Read(t []byte) (int, error) {
	j, e := json.Marshal(sp)
	if e != nil {
		return 0, e
	}

	if len(t) < len(j) {
		return len(t), nil
	}

	copy(t, j)
	return len(j), io.EOF
}

func (sp *SequenceAddProspect) Close() error {
	return nil
}

func (s *SequenceInstance) AddProspect(id int, pids ...int) (SequenceAddProspectResponse, error) {
	seq := SequenceAddProspectResponse{}

	if s.Client == nil {
		return seq, errors.New("You must assign a HTTP client.")
	}

	sp := SequenceAddProspect{}

	for _, v := range pids {
		sp.Data.Relationships.Prospects =
			append(sp.Data.Relationships.Prospects, SequenceAddProspectProspect{
				Data: SequenceAddProspectProspectData{
					ID: strconv.Itoa(v),
				},
			})
	}

	req, e := http.NewRequest("PATCH", "https://api.outreach.io/1.0/sequences/"+strconv.Itoa(id), &sp)
	if e != nil {
		return seq, e
	}

	resp, e := s.Client.Do(req)
	if e != nil {
		return seq, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return seq, e
	}

	e = json.Unmarshal(body, &seq)
	if e != nil {
		return seq, e
	}

	if len(seq.Errors) > 0 {
		return seq, fmt.Errorf("Got error response: %+v\n", seq.Errors)
	}

	return seq, nil
}

func (s *SequenceInstance) Get(opts ...int) (SequenceResponse, error) {
	seq := SequenceResponse{}

	if s.Client == nil {
		return seq, errors.New("You must assign a HTTP client.")
	}

	resp, e := s.Client.Get("https://api.outreach.io/1.0/sequences")
	if e != nil {
		return seq, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return seq, e
	}

	e = json.Unmarshal(body, &seq)
	if e != nil {
		return seq, e
	}

	if len(seq.Errors) > 0 {
		return seq, fmt.Errorf("Got error response: %+v\n", seq.Errors)
	}

	return seq, nil
}
<|endoftext|>


<commit_before>package sequence

import (
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	ErrNotSeqfile    = errors.New("not a sequence file")
	ErrFrameExists   = errors.New("frame exists")
	ErrNegativeFrame = errors.New("nagative frame")
)

var Split = regexp.MustCompile(`(.*\D)*(\d+)(.+?)$`)

var (
	FmtSharp = func(pre, digits, post string) string {
		return pre + strings.Repeat("#", len(digits)) + post
	}
	FmtDollarF = func(pre, digits, post string) string {
		return pre + "$F" + strconv.Itoa(len(digits)) + post
	}
	FmtPercentD = func(pre, digits, post string) string {
		return pre + "%d" + strconv.Itoa(len(digits)) + post
	}
)

type Manager struct {
	Seqs       map[string]*Seq
	formatting func(pre, digits, post string) string
}

func NewManager(formatting func(pre, digits, post string) string) *Manager {
	return &Manager{
		Seqs:       make(map[string]*Seq),
		formatting: formatting,
	}
}

func (m *Manager) Add(fname string) error {
	sub := Split.FindStringSubmatch(fname)
	if sub == nil {
		return ErrNotSeqfile
	}
	pre := sub[0]
	digits := sub[1]
	post := sub[2]

	name := m.formatting(pre, digits, post)
	frame, _ := strconv.Atoi(digits)

	s, ok := m.Seqs[name]
	if !ok {
		s = NewSeq()
		m.Seqs[name] = s
	}
	return s.AddFrame(frame)
}

func (m *Manager) SeqNames() []string {
	names := []string{}
	for n := range m.Seqs {
		names = append(names, n)
	}
	sort.Strings(names)
	return names
}

func (m *Manager) String() string {
	str := ""
	for _, n := range m.SeqNames() {
		if str != "" {
			str += "\n"
		}
		str += fmt.Sprintf("%s %s", n, m.Seqs[n])
	}
	return str
}

type Seq struct {
	frames map[int]struct{}
}

func NewSeq() *Seq {
	return &Seq{
		frames: make(map[int]struct{}),
	}
}

func (s *Seq) AddFrame(f int) error {
	if f < 0 {
		return ErrNegativeFrame
	}
	if _, ok := s.frames[f]; ok {
		return ErrFrameExists
	}
	s.frames[f] = struct{}{}
	return nil
}

func (s *Seq) Ranges() []*Range {
	if len(s.frames) == 0 {
		return []*Range{}
	}

	frames := []int{}
	for f := range s.frames {
		frames = append(frames, f)
	}
	sort.Ints(frames)

	rngs := []*Range{}
	r := NewRange(frames[0])
	rngs = append(rngs, r)
	for _, f := range frames[1:] {
		ok := r.Extend(f)
		if !ok {
			r = NewRange(f)
			rngs = append(rngs, r)
		}
	}
	return rngs
}

func (s *Seq) String() string {
	str := ""
	rngs := s.Ranges()
	for _, r := range rngs {
		if str != "" {
			str += " "
		}
		if r.minf == r.maxf {
			str += fmt.Sprintf("%d", r.minf)
		} else {
			str += fmt.Sprintf("%d-%d", r.minf, r.maxf)
		}
	}
	return str
}

type Range struct {
	minf int
	maxf int
}

func NewRange(f int) *Range {
	return &Range{
		minf: f,
		maxf: f,
	}
}

func (r *Range) Extend(f int) bool {
	if f != r.maxf+1 {
		return false
	}
	r.maxf = f
	return true
}
<commit_msg>add Splitter.<commit_after>package sequence

import (
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	ErrNotSeqfile    = errors.New("not a sequence file")
	ErrFrameExists   = errors.New("frame exists")
	ErrNegativeFrame = errors.New("nagative frame")
)

// Splitter is a file name splitter.
type Splitter struct {
	re *regexp.Regexp
}

// reSplit is default regular expression for Splitter.
var reSplit = regexp.MustCompile(`(.*\D)*(\d+)(.*?)$`)

// NewSplitter creates a new splitter.
func NewSplitter() *Splitter {
	return &Splitter{
		re: reSplit,
	}
}

// SetRegexp let users of this package to make their own splitter.
// Splitter always assume that the regular expression is right.
// So who makes their own splitter should ensure that it is right.
func (s *Splitter) SetRegexp(re *regexp.Regexp) {
	s.re = re
}

// Split takes file name and splits it into 3 parts,
// which is pre, digits, and post.
func (s *Splitter) Split(fname string) (pre, digits, post string, err error) {
	m := s.re.FindStringSubmatch(fname)
	if m == nil {
		return "", "", "", ErrNotSeqfile
	}
	return m[1], m[2], m[3], nil
}

// Fmt{Sharp, DollarF, PrecentD} are pre-defined formatter, that covers most user's need.
var (
	FmtSharp = func(pre, digits, post string) string {
		return pre + strings.Repeat("#", len(digits)) + post
	}
	FmtDollarF = func(pre, digits, post string) string {
		return pre + "$F" + strconv.Itoa(len(digits)) + post
	}
	FmtPercentD = func(pre, digits, post string) string {
		return pre + "%d" + strconv.Itoa(len(digits)) + post
	}
)

type Manager struct {
	Seqs map[string]*Seq

	splitter   *Splitter
	formatting func(pre, digits, post string) string
}

func NewManager(splitter *Splitter, formatting func(pre, digits, post string) string) *Manager {
	return &Manager{
		Seqs:       make(map[string]*Seq),
		splitter:   splitter,
		formatting: formatting,
	}
}

func (m *Manager) Add(fname string) error {
	pre, digits, post, err := m.splitter.Split(fname)
	if err != nil {
		return err
	}

	name := m.formatting(pre, digits, post)
	frame, _ := strconv.Atoi(digits)

	s, ok := m.Seqs[name]
	if !ok {
		s = NewSeq()
		m.Seqs[name] = s
	}
	return s.AddFrame(frame)
}

func (m *Manager) SeqNames() []string {
	names := []string{}
	for n := range m.Seqs {
		names = append(names, n)
	}
	sort.Strings(names)
	return names
}

func (m *Manager) String() string {
	str := ""
	for _, n := range m.SeqNames() {
		if str != "" {
			str += "\n"
		}
		str += fmt.Sprintf("%s %s", n, m.Seqs[n])
	}
	return str
}

type Seq struct {
	frames map[int]struct{}
}

func NewSeq() *Seq {
	return &Seq{
		frames: make(map[int]struct{}),
	}
}

func (s *Seq) AddFrame(f int) error {
	if f < 0 {
		return ErrNegativeFrame
	}
	if _, ok := s.frames[f]; ok {
		return ErrFrameExists
	}
	s.frames[f] = struct{}{}
	return nil
}

func (s *Seq) Ranges() []*Range {
	if len(s.frames) == 0 {
		return []*Range{}
	}

	frames := []int{}
	for f := range s.frames {
		frames = append(frames, f)
	}
	sort.Ints(frames)

	rngs := []*Range{}
	r := NewRange(frames[0])
	rngs = append(rngs, r)
	for _, f := range frames[1:] {
		ok := r.Extend(f)
		if !ok {
			r = NewRange(f)
			rngs = append(rngs, r)
		}
	}
	return rngs
}

func (s *Seq) String() string {
	str := ""
	rngs := s.Ranges()
	for _, r := range rngs {
		if str != "" {
			str += " "
		}
		if r.minf == r.maxf {
			str += fmt.Sprintf("%d", r.minf)
		} else {
			str += fmt.Sprintf("%d-%d", r.minf, r.maxf)
		}
	}
	return str
}

type Range struct {
	minf int
	maxf int
}

func NewRange(f int) *Range {
	return &Range{
		minf: f,
		maxf: f,
	}
}

func (r *Range) Extend(f int) bool {
	if f != r.maxf+1 {
		return false
	}
	r.maxf = f
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"sync"
)

// The authorization sequence, required for someone without a ~/.getconfig
func sequence_authorize(env Env) Env {
	log.Println("Begin authorization sequence...")

	// Let the user know what will happen.
	fmt.Println(`
Your username and password will be used once to obtain a unique
authorization token from GitHub's API, which will be stored in
~/.getconfig.
`)

	env = askForCredentials(env)

	log.Println(env.Config.Username, env.Config.Token)

	// Make the configuration file.
	createConfiguration(env)

	return env
}

// The update sequence, which retrieves the repos and fetches or clones
// each of them.
func sequence_update(env Env) {
	log.Println("Begin repository update sequence...")

	repos := listRemoteRepostories(env)

	fetches := []string{}
	clones := []string{}
	errors := []string{}
	ignores := []string{}

	var wg sync.WaitGroup
	for _, repo := range repos {
		wg.Add(1)

		go func(repo Repo) {
			fmt.Println(repo.Name())

			switch checkRepo(repo, env) {
			case "fetch":
				fetches = append(fetches, repo.Name())
			case "clone":
				clones = append(clones, repo.Name())
			case "error":
				errors = append(errors, repo.Name())
			case "ignore":
				ignores = append(ignores, repo.Name())
			}
			wg.Done()
		}(repo)
	}

	wg.Wait()

	fmt.Println("Updated repositories:", len(fetches))

	fmt.Println("New repositories:", len(clones))
	for _, repo := range clones {
		fmt.Println("\t", repo)
	}

	if len(errors) > 0 {
		fmt.Println(len(errors), "error(s) encountered.")
	}
}

// The check sequence, which goes through the basic health checks for
// `get` to succesfully function.
func sequence_checks(env Env) Env {
	log.Println("Begin check sequence...")

	// Inject configuration
	env.Config = injectConfiguration()

	// Check supplied path
	checkPath(env)

	// Check Configuration
	checkConfiguration(env)

	// Check configured path
	checkPath(env)

	return env
}
<commit_msg>Clean debug messages<commit_after>package main

import (
	"fmt"
	"log"
	"sync"
)

// The authorization sequence, required for someone without a ~/.getconfig
func sequence_authorize(env Env) Env {
	log.Println("Begin authorization sequence...")

	// Let the user know what will happen.
	fmt.Println(`
Your username and password will be used once to obtain a unique
authorization token from GitHub's API, which will be stored in
~/.getconfig.
`)

	env = askForCredentials(env)

	log.Println(env.Config.Username, env.Config.Token)

	// Make the configuration file.
	createConfiguration(env)

	return env
}

// The update sequence, which retrieves the repos and fetches or clones
// each of them.
func sequence_update(env Env) {
	log.Println("Begin repository update sequence...")

	repos := listRemoteRepostories(env)

	fetches := []string{}
	clones := []string{}
	errors := []string{}
	ignores := []string{}

	var wg sync.WaitGroup
	for _, repo := range repos {
		wg.Add(1)

		go func(repo Repo) {
			switch checkRepo(repo, env) {
			case "fetch":
				fetches = append(fetches, repo.Name())
			case "clone":
				clones = append(clones, repo.Name())
			case "error":
				errors = append(errors, repo.Name())
			case "ignore":
				ignores = append(ignores, repo.Name())
			}
			wg.Done()
		}(repo)
	}

	wg.Wait()

	fmt.Println("Updated repositories:", len(fetches))

	fmt.Println("New repositories:", len(clones))
	for _, repo := range clones {
		fmt.Println("\t", repo)
	}

	if len(errors) > 0 {
		fmt.Println(len(errors), "error(s) encountered.")
	}
}

// The check sequence, which goes through the basic health checks for
// `get` to succesfully function.
func sequence_checks(env Env) Env {
	log.Println("Begin check sequence...")

	// Inject configuration
	env.Config = injectConfiguration()

	// Check supplied path
	checkPath(env)

	// Check Configuration
	checkConfiguration(env)

	// Check configured path
	checkPath(env)

	return env
}
<|endoftext|>


<commit_before>package sequence

import (
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	ErrNotSeqfile    = errors.New("not a sequence file")
	ErrFrameExists   = errors.New("frame exists")
	ErrNegativeFrame = errors.New("nagative frame")
)

// Splitter is a file name splitter.
type Splitter struct {
	re *regexp.Regexp
}

// reDefaultSplit is default regular expression for Splitter.
var reDefaultSplit = regexp.MustCompile(`(.*\D)*(\d+)(.*?)$`)

// DefaultSplitter is a default splitter for this package.
//
// User could create their own splitter. See NewSplitter.
var DefaultSplitter = NewSplitter(reDefaultSplit)

// NewSplitter creates a new custom splitter.
//
// Splitter always assumes that it's regular expression is right.
// So who makes their own splitter should ensure that it is right.
func NewSplitter(re *regexp.Regexp) *Splitter {
	return &Splitter{
		re: re,
	}
}

// Split takes a file name and splits it into 3 parts,
// which is pre, digits, and post.
func (s *Splitter) Split(fname string) (pre, digits, post string, err error) {
	m := s.re.FindStringSubmatch(fname)
	if m == nil {
		return "", "", "", ErrNotSeqfile
	}
	return m[1], m[2], m[3], nil
}

// Fmt{Sharp, DollarF, PrecentD} are pre-defined formatter,
// that covers most user's need.
var (
	FmtSharp = func(pre, digits, post string) string {
		return pre + strings.Repeat("#", len(digits)) + post
	}
	FmtDollarF = func(pre, digits, post string) string {
		return pre + "$F" + strconv.Itoa(len(digits)) + post
	}
	FmtPercentD = func(pre, digits, post string) string {
		return pre + "%0" + strconv.Itoa(len(digits)) + "d" + post
	}
)

// A Manager is a sequence manager.
type Manager struct {
	Seqs map[string]*Seq

	splitter   *Splitter
	formatting func(pre, digits, post string) string
}

// NewManager creates a new sequence manager.
func NewManager(splitter *Splitter, formatting func(pre, digits, post string) string) *Manager {
	return &Manager{
		Seqs:       make(map[string]*Seq),
		splitter:   splitter,
		formatting: formatting,
	}
}

// Add adds a file to the manager.
//
// If the file's sequence is not exist yet,
// it will create a new sequence automatically.
func (m *Manager) Add(fname string) error {
	pre, digits, post, err := m.splitter.Split(fname)
	if err != nil {
		return err
	}

	name := m.formatting(pre, digits, post)
	frame, _ := strconv.Atoi(digits)

	s, ok := m.Seqs[name]
	if !ok {
		s = NewSeq()
		m.Seqs[name] = s
	}
	return s.AddFrame(frame)
}

// SeqNames returns it's sequence names in ascending order.
func (m *Manager) SeqNames() []string {
	names := []string{}
	for n := range m.Seqs {
		names = append(names, n)
	}
	sort.Strings(names)
	return names
}

// String returns a string that shows it's sequences.
//
// It will be multiple lines if it has more than one sequence.
func (m *Manager) String() string {
	str := ""
	for _, n := range m.SeqNames() {
		if str != "" {
			str += "\n"
		}
		str += fmt.Sprintf("%s %s", n, m.Seqs[n])
	}
	return str
}

// A Seq is a frame sequence. It does not hold a sequence name.
type Seq struct {
	frames map[int]struct{}
}

// NewSeq creates a new sequence.
func NewSeq() *Seq {
	return &Seq{
		frames: make(map[int]struct{}),
	}
}

// AddFrame adds a frame into sequence.
//
// It treats negative frames are invalid.
// So returns ErrNegativeFrame when it takes a negative frame.
func (s *Seq) AddFrame(f int) error {
	if f < 0 {
		return ErrNegativeFrame
	}
	if _, ok := s.frames[f]; ok {
		return ErrFrameExists
	}
	s.frames[f] = struct{}{}
	return nil
}

// Ranges converts a sequence to several contiguous ranges.
func (s *Seq) Ranges() []*Range {
	if len(s.frames) == 0 {
		return []*Range{}
	}

	frames := []int{}
	for f := range s.frames {
		frames = append(frames, f)
	}
	sort.Ints(frames)

	rngs := []*Range{}
	r := NewRange(frames[0])
	rngs = append(rngs, r)
	for _, f := range frames[1:] {
		ok := r.Extend(f)
		if !ok {
			r = NewRange(f)
			rngs = append(rngs, r)
		}
	}
	return rngs
}

// String expresses a sequence using ranges.
func (s *Seq) String() string {
	str := ""
	rngs := s.Ranges()
	for _, r := range rngs {
		if str != "" {
			str += " "
		}
		str += r.String()
	}
	return str
}

// Range is a contiguous frame range,
// which includes Max frame.
type Range struct {
	Min int
	Max int
}

// NewRange creates a new range.
func NewRange(f int) *Range {
	return &Range{
		Min: f,
		Max: f,
	}
}

// Extend extends a range by one, only if,
// input frame is bigger than current max frame by 1.
// When it extends, it returns true, or it returns false.
func (r *Range) Extend(f int) bool {
	if f != r.Max+1 {
		return false
	}
	r.Max = f
	return true
}

// String expresses the range with dash. Like "1-10".
// But if the min and max is same, it will just show one. Like "5".
func (r *Range) String() string {
	if r.Min == r.Max {
		return fmt.Sprintf("%d", r.Min)
	}
	return fmt.Sprintf("%d-%d", r.Min, r.Max)
}
<commit_msg>fix comments.<commit_after>package sequence

import (
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	ErrNotSeqfile    = errors.New("not a sequence file")
	ErrFrameExists   = errors.New("frame exists")
	ErrNegativeFrame = errors.New("nagative frame")
)

// Splitter is a file name splitter.
type Splitter struct {
	// re catches sequence file name
	// and groups the file name into 3 parts (pre, digits, post).
	// When it does not match, the file will treated as non-sequece file.
	//
	// Note: If it does not have 3 sub groups, it will panic.
	re *regexp.Regexp
}

// reDefaultSplit is regular expression for DefaultSplitter.
// It finds right most digit strings and it's pre and post parts.
var reDefaultSplit = regexp.MustCompile(`(.*\D)*(\d+)(.*?)$`)

// DefaultSplitter is a default splitter for this package.
//
// User could create their own splitter. See NewSplitter.
var DefaultSplitter = NewSplitter(reDefaultSplit)

// NewSplitter creates a new splitter.
//
// Splitter assumes it's regular expression could catch sequence file name
// and groups the file name into 3 parts (pre, digits, post).
// When it does not match, the file will treated as non-sequece file.
//
// Note: If the regular expression does not have 3 sub groups, it will panic.
func NewSplitter(re *regexp.Regexp) *Splitter {
	return &Splitter{
		re: re,
	}
}

// Split takes a file name and splits it into 3 parts,
// which is pre, digits, and post.
// It returns error if the file name does not look like a sequence file.
func (s *Splitter) Split(fname string) (pre, digits, post string, err error) {
	m := s.re.FindStringSubmatch(fname)
	if m == nil {
		return "", "", "", ErrNotSeqfile
	}
	return m[1], m[2], m[3], nil
}

// Fmt{Sharp, DollarF, PrecentD} are pre-defined formatter,
// that covers most user's need.
var (
	FmtSharp = func(pre, digits, post string) string {
		return pre + strings.Repeat("#", len(digits)) + post
	}
	FmtDollarF = func(pre, digits, post string) string {
		return pre + "$F" + strconv.Itoa(len(digits)) + post
	}
	FmtPercentD = func(pre, digits, post string) string {
		return pre + "%0" + strconv.Itoa(len(digits)) + "d" + post
	}
)

// A Manager is a sequence manager.
type Manager struct {
	Seqs map[string]*Seq

	splitter   *Splitter
	formatting func(pre, digits, post string) string
}

// NewManager creates a new sequence manager.
func NewManager(splitter *Splitter, formatting func(pre, digits, post string) string) *Manager {
	return &Manager{
		Seqs:       make(map[string]*Seq),
		splitter:   splitter,
		formatting: formatting,
	}
}

// Add adds a file to the manager.
//
// If the file's sequence is not exist yet,
// it will create a new sequence automatically.
func (m *Manager) Add(fname string) error {
	pre, digits, post, err := m.splitter.Split(fname)
	if err != nil {
		return err
	}

	name := m.formatting(pre, digits, post)
	frame, _ := strconv.Atoi(digits)

	s, ok := m.Seqs[name]
	if !ok {
		s = NewSeq()
		m.Seqs[name] = s
	}
	return s.AddFrame(frame)
}

// SeqNames returns it's sequence names in ascending order.
func (m *Manager) SeqNames() []string {
	names := []string{}
	for n := range m.Seqs {
		names = append(names, n)
	}
	sort.Strings(names)
	return names
}

// String returns a string that shows it's sequences.
//
// It will be multiple lines if it has more than one sequence.
func (m *Manager) String() string {
	str := ""
	for _, n := range m.SeqNames() {
		if str != "" {
			str += "\n"
		}
		str += fmt.Sprintf("%s %s", n, m.Seqs[n])
	}
	return str
}

// A Seq is a frame sequence. It does not hold a sequence name.
type Seq struct {
	frames map[int]struct{}
}

// NewSeq creates a new sequence.
func NewSeq() *Seq {
	return &Seq{
		frames: make(map[int]struct{}),
	}
}

// AddFrame adds a frame into sequence.
//
// It treats negative frames are invalid.
// So returns ErrNegativeFrame when it takes a negative frame.
func (s *Seq) AddFrame(f int) error {
	if f < 0 {
		return ErrNegativeFrame
	}
	if _, ok := s.frames[f]; ok {
		return ErrFrameExists
	}
	s.frames[f] = struct{}{}
	return nil
}

// Ranges converts a sequence to several contiguous ranges.
func (s *Seq) Ranges() []*Range {
	if len(s.frames) == 0 {
		return []*Range{}
	}

	frames := []int{}
	for f := range s.frames {
		frames = append(frames, f)
	}
	sort.Ints(frames)

	rngs := []*Range{}
	r := NewRange(frames[0])
	rngs = append(rngs, r)
	for _, f := range frames[1:] {
		ok := r.Extend(f)
		if !ok {
			r = NewRange(f)
			rngs = append(rngs, r)
		}
	}
	return rngs
}

// String expresses a sequence using ranges.
func (s *Seq) String() string {
	str := ""
	rngs := s.Ranges()
	for _, r := range rngs {
		if str != "" {
			str += " "
		}
		str += r.String()
	}
	return str
}

// Range is a contiguous frame range,
// which includes Max frame.
type Range struct {
	Min int
	Max int
}

// NewRange creates a new range.
func NewRange(f int) *Range {
	return &Range{
		Min: f,
		Max: f,
	}
}

// Extend extends a range by one, only if,
// input frame is bigger than current max frame by 1.
// When it extends, it returns true, or it returns false.
func (r *Range) Extend(f int) bool {
	if f != r.Max+1 {
		return false
	}
	r.Max = f
	return true
}

// String expresses the range with dash. Like "1-10".
// But if the min and max are same, it will just show one. Like "5".
func (r *Range) String() string {
	if r.Min == r.Max {
		return fmt.Sprintf("%d", r.Min)
	}
	return fmt.Sprintf("%d-%d", r.Min, r.Max)
}
<|endoftext|>


<commit_before>package whiplash

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"firepear.net/pclient"
)

// These are our Svc types, which are basically the types of ceph
// daemons.
const (
	MON = iota
	RGW
	OSD
)

var (
	// the list of admin socket commands we know
	cephcmds = map[string][]byte{
		"version": []byte("{\"prefix\":\"version\"}\000"),
		"perf dump": []byte("{\"prefix\":\"perf dump\"}\000"),
	}
)

// Svc represents a Ceph service
type Svc struct {
	Core *SvcCore

	// Sock is the admin socket for the service
	Sock string

	// Err holds the error (if any) from the Ping() check
	Err error

	// Resp receives response data from Query()
	Resp []byte

	// configuration for connections to the admin socket
	pcconf *pclient.Config
	// b0 is where we read the message length into
	b0 []byte
	// mlen is the unpacked length from b0
	mlen int32
	// mread is the number of bytes read in the message so far
	mread int32
	// b1 is the buffer we read into from the network
	b1 []byte
	// b2 accumulates data from b1
	b2 []byte
}

// SvcCore is the universal core data shared by all service
// descriptions.
type SvcCore struct {
	// Name is the name/ID of the service
	Name string `json:"name"`

	// Type is the service/daemon type: MON, RGW, OSD
	Type int `json:"type"`

	// Host is the machine where the service runs
	Host string `json:"host"`

	// Version is the Ceph version of the service.
	Version string `json:"version"`

	// Reporting shows if a service is contactable and responsive
	Reporting bool `json:"reporting"`
}

// getCephServices examines wlc.CephConf and populates wlc.Svcs
func (wlc *WLConfig) getCephServices() {
	wlc.Svcs = make(map[string]*Svc)
	// iterate over CephConf, adding OSDs and RGWs
	for k, m := range wlc.CephConf {
		s := &Svc{Core: &SvcCore{Name: k}, b0: make([]byte, 4)}
		switch {
		case strings.HasPrefix(k, "osd."):
			s.Core.Type = OSD
			s.Core.Host = m["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		case strings.HasPrefix(k, "client.radosgw"):
			s.Core.Type = RGW
			s.Core.Host = os.Getenv("HOSTNAME")
			if rsp, ok := m["rgw socket path"]; ok {
				s.Sock = rsp
			} else {
				s.Sock = strings.Replace(m["admin socket"], "$name", k, 1)
			}
		case strings.HasPrefix(k, "mon." + os.Getenv("HOSTNAME")):
			s.Core.Type = MON
			s.Core.Host = wlc.CephConf[k]["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		}
		// only add defined services to Svcs when the admin socket exists
		if _, err := os.Stat(s.Sock); err == nil {
			s.pcconf = &pclient.Config{Addr: s.Sock, Timeout: 100, OmitPrefix: true}
			wlc.Svcs[k] = s
		}
	}
}

// Ping sends a version request to a Ceph service. It acts as the test
// for whether a service is reporting. When successful, it sets
// Reporting to 'true' and sets the service's Version. When it fails,
// Reporting is set to 'false', and Err is set to the returned error.
func (s *Svc) Ping() {
	err := s.Query("version")
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return
	}
	var vs cephVersion
	err = json.Unmarshal(s.Resp, &vs)
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return
	}
	s.Core.Reporting = true
	s.Err = nil
	s.Core.Version = vs.Version
}

// Stat is the wrapper function for performing Svc stat updates. It
// handles commonalities and then calls the service-appropriate stat
// function to gather data.
func (s *Svc) Stat() json.RawMessage {
	err := s.Query("perf dump")
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return nil
	}
	var statdata json.RawMessage
	if s.Core.Type == OSD {
		statdata = s.StatOsd()
	}
	// TODO handle MON, RGW
	return statdata
}

// StatOsd is the OSD-specific stat update data gathering routine.
func (s *Svc) StatOsd() json.RawMessage {
	var os OsdStat
	var pd cephOsdPerfDump
	// vivify the osd section of the perf dump
	err := json.Unmarshal(s.Resp, &pd)
	if err != nil {
		s.Err = err
		return nil
	}
	// gather data from it
	os.BytesUsed = pd.Osd.StatBytes
	os.BytesAvail = pd.Osd.StatBytesAvail
	os.PgPrimary = pd.Osd.NumPgPrimary
	os.PgReplica = pd.Osd.NumPgReplica
	// and json-encode our stat struct
	var raw json.RawMessage
	raw, err = json.Marshal(os)
	if err != nil {
		s.Err = err
		return nil
	}
	return raw
}

// Query sends a request to a Ceph service and reads the result.
func (s *Svc) Query(req string) error {
	// make sure we know this command
	cmd, ok := cephcmds[req]
	if !ok {
		return fmt.Errorf("unknown request '%v'\n", req)
	}

	// make the connection
	c, err := pclient.NewUnix(s.pcconf)
	if err != nil {
		return fmt.Errorf("could not connect to sock %s: %s\n", s.Sock, err)
	}
	defer c.Close()

	// dispatch and return
	s.Resp, err = c.Dispatch(cmd)
	if err != nil {
		return fmt.Errorf("could not read reply on %s: %s\n", s.Sock, err)
	}
	return err
}
<commit_msg>fixes for upstream petrel changes<commit_after>package whiplash

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"firepear.net/petrel"
)

// These are our Svc types, which are basically the types of ceph
// daemons.
const (
	MON = iota
	RGW
	OSD
)

var (
	// the list of admin socket commands we know
	cephcmds = map[string][]byte{
		"version": []byte("{\"prefix\":\"version\"}\000"),
		"perf dump": []byte("{\"prefix\":\"perf dump\"}\000"),
	}
)

// Svc represents a Ceph service
type Svc struct {
	Core *SvcCore
	// Sock is the admin socket for the service
	Sock string
	// Err holds the error (if any) from the Ping() check
	Err error
	// Resp receives response data from Query()
	Resp []byte
	// configuration for connections to the admin socket
	pcconf *petrel.ClientConfig
        // b0 is where we read the message length into
        b0 []byte
        // mlen is the unpacked length from b0
        mlen int32
        // mread is the number of bytes read in the message so far
        mread int32
        // b1 is the buffer we read into from the network
        b1 []byte
        // b2 accumulates data from b1
        b2 []byte
}

// SvcCore is the universal core data shared by all service
// descriptions.
type SvcCore struct {
	// Name is the name/ID of the service
	Name string `json:"name"`

	// Type is the service/daemon type: MON, RGW, OSD
	Type int `json:"type"`

	// Host is the machine where the service runs
	Host string `json:"host"`

	// Version is the Ceph version of the service.
	Version string `json:"version"`

	// Reporting shows if a service is contactable and responsive
	Reporting bool `json:"reporting"`
}

// getCephServices examines wlc.CephConf and populates wlc.Svcs
func (wlc *WLConfig) getCephServices() {
	wlc.Svcs = make(map[string]*Svc)
	// iterate over CephConf, adding OSDs and RGWs
	for k, m := range wlc.CephConf {
		s := &Svc{Core: &SvcCore{Name: k}}
		switch {
		case strings.HasPrefix(k, "osd."):
			s.Core.Type = OSD
			s.Core.Host = m["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		case strings.HasPrefix(k, "client.radosgw"):
			s.Core.Type = RGW
			s.Core.Host = os.Getenv("HOSTNAME")
			if rsp, ok := m["rgw socket path"]; ok {
				s.Sock = rsp
			} else {
				s.Sock = strings.Replace(m["admin socket"], "$name", k, 1)
			}
		case strings.HasPrefix(k, "mon." + os.Getenv("HOSTNAME")):
			s.Core.Type = MON
			s.Core.Host = wlc.CephConf[k]["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		}
		// only add defined services to Svcs when the admin
		// socket exists

		// TODO this shouldn't even be a petrel
		// client; this is where the custom client for talking
		// to Ceph goes.
		if _, err := os.Stat(s.Sock); err == nil {
			s.pcconf = &petrel.ClientConfig{Addr: s.Sock, Timeout: 100}
			wlc.Svcs[k] = s
		}
	}
}

// Ping sends a version request to a Ceph service. It acts as the test
// for whether a service is reporting. When successful, it sets
// Reporting to 'true' and sets the service's Version. When it fails,
// Reporting is set to 'false', and Err is set to the returned error.
func (s *Svc) Ping() {
	err := s.Query("version")
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return
	}
	var vs cephVersion
	err = json.Unmarshal(s.Resp, &vs)
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return
	}
	s.Core.Reporting = true
	s.Err = nil
	s.Core.Version = vs.Version
}

// Stat is the wrapper function for performing Svc stat updates. It
// handles commonalities and then calls the service-appropriate stat
// function to gather data.
func (s *Svc) Stat() json.RawMessage {
	err := s.Query("perf dump")
	if err != nil {
		s.Core.Reporting = false
		s.Err = err
		return nil
	}
	var statdata json.RawMessage
	if s.Core.Type == OSD {
		statdata = s.StatOsd()
	}
	// TODO handle MON, RGW
	return statdata
}

// StatOsd is the OSD-specific stat update data gathering routine.
func (s *Svc) StatOsd() json.RawMessage {
	var os OsdStat
	var pd cephOsdPerfDump
	// vivify the osd section of the perf dump
	err := json.Unmarshal(s.Resp, &pd)
	if err != nil {
		s.Err = err
		return nil
	}
	// gather data from it
	os.BytesUsed = pd.Osd.StatBytes
	os.BytesAvail = pd.Osd.StatBytesAvail
	os.PgPrimary = pd.Osd.NumPgPrimary
	os.PgReplica = pd.Osd.NumPgReplica
	// and json-encode our stat struct
	var raw json.RawMessage
	raw, err = json.Marshal(os)
	if err != nil {
		s.Err = err
		return nil
	}
	return raw
}

// Query sends a request to a Ceph service and reads the result.
func (s *Svc) Query(req string) error {
	// make sure we know this command
	cmd, ok := cephcmds[req]
	if !ok {
		return fmt.Errorf("unknown request '%v'\n", req)
	}

	// make the connection
	c, err := petrel.UnixClient(s.pcconf)
	if err != nil {
		return fmt.Errorf("could not connect to sock %s: %s\n", s.Sock, err)
	}
	defer c.Quit()

	// dispatch and return
	s.Resp, err = c.Dispatch(cmd)
	if err != nil {
		return fmt.Errorf("could not read reply on %s: %s\n", s.Sock, err)
	}
	return err
}
<|endoftext|>


<commit_before>package cloudns

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/miekg/dns/dnsutil"

	"github.com/StackExchange/dnscontrol/v3/models"
	"github.com/StackExchange/dnscontrol/v3/pkg/diff"
	"github.com/StackExchange/dnscontrol/v3/providers"
)

/*
ClouDNS API DNS provider:
Info required in `creds.json`:
   - auth-id or sub-auth-id
   - auth-password
*/

// NewCloudns creates the provider.
func NewCloudns(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	c := &cloudnsProvider{}

	c.creds.id, c.creds.password, c.creds.subid = m["auth-id"], m["auth-password"], m["sub-auth-id"]

	if (c.creds.id == "" && c.creds.subid == "") || c.creds.password == "" {
		return nil, fmt.Errorf("missing ClouDNS auth-id or sub-auth-id and auth-password")
	}

	// Get a domain to validate authentication
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}

	return c, nil
}

var features = providers.DocumentationNotes{
	providers.DocDualHost:            providers.Unimplemented(),
	providers.DocOfficiallySupported: providers.Cannot(),
	providers.DocCreateDomains:       providers.Can(),
	providers.CanUseAlias:            providers.Can(),
	providers.CanUseSRV:              providers.Can(),
	providers.CanUseSSHFP:            providers.Can(),
	providers.CanUseCAA:              providers.Can(),
	providers.CanUseTLSA:             providers.Can(),
	providers.CanUseNAPTR:            providers.Can(),
	providers.CanUsePTR:              providers.Can(),
	providers.CanGetZones:            providers.Can(),
	providers.CanUseDSForChildren:    providers.Can(),
	//providers.CanUseDS:               providers.Can(),  // in ClouDNS we can add  DS record just for a subdomain(child)
}

func init() {
	fns := providers.DspFuncs{
		Initializer:   NewCloudns,
		RecordAuditor: AuditRecords,
	}
	providers.RegisterDomainServiceProviderType("CLOUDNS", fns, features)
}

// GetNameservers returns the nameservers for a domain.
func (c *cloudnsProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
	if len(c.nameserversNames) == 0 {
		c.fetchAvailableNameservers()
	}
	return models.ToNameservers(c.nameserversNames)
}

// GetDomainCorrections returns the corrections for a domain.
func (c *cloudnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	dc, err := dc.Copy()
	if err != nil {
		return nil, err
	}

	dc.Punycode()

	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	domainID, ok := c.domainIndex[dc.Name]
	if !ok {
		return nil, fmt.Errorf("'%s' not a zone in ClouDNS account", dc.Name)
	}

	existingRecords, err := c.GetZoneRecords(dc.Name)
	if err != nil {
		return nil, err
	}
	// Normalize
	models.PostProcessRecords(existingRecords)

	// Get a list of available TTL values.
	// The TTL list needs to be obtained for each domain, so get it first here.
	c.fetchAvailableTTLValues(dc.Name)
	// ClouDNS can only be specified from a specific TTL list, so change the TTL in advance.
	for _, record := range dc.Records {
		record.TTL = fixTTL(record.TTL)
	}

	differ := diff.New(dc)
	_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
	if err != nil {
		return nil, err
	}

	var corrections []*models.Correction

	// Deletes first so changing type works etc.
	for _, m := range del {
		id := m.Existing.Original.(*domainRecord).ID
		corr := &models.Correction{
			Msg: fmt.Sprintf("%s, ClouDNS ID: %s", m.String(), id),
			F: func() error {
				return c.deleteRecord(domainID, id)
			},
		}
		// at ClouDNS, we MUST have a NS for a DS
		// So, when deleting, we must delete the DS first, otherwise deleting the NS throws an error
		if m.Existing.Type == "DS" {
			// type DS is prepended - so executed first
			corrections = append([]*models.Correction{corr}, corrections...)
		} else {
			corrections = append(corrections, corr)
		}
	}

	var createCorrections []*models.Correction
	for _, m := range create {
		req, err := toReq(m.Desired)
		if err != nil {
			return nil, err
		}

		corr := &models.Correction{
			Msg: m.String(),
			F: func() error {
				return c.createRecord(domainID, req)
			},
		}
		// at ClouDNS, we MUST have a NS for a DS
		// So, when creating, we must create the NS first, otherwise creating the DS throws an error
		if m.Desired.Type == "NS" {
			// type NS is prepended - so executed first
			createCorrections = append([]*models.Correction{corr}, createCorrections...)
		} else {
			createCorrections = append(createCorrections, corr)
		}
	}
	corrections = append(corrections, createCorrections...)

	for _, m := range modify {
		id := m.Existing.Original.(*domainRecord).ID
		req, err := toReq(m.Desired)
		if err != nil {
			return nil, err
		}

		corr := &models.Correction{
			Msg: fmt.Sprintf("%s, ClouDNS ID: %s: ", m.String(), id),
			F: func() error {
				return c.modifyRecord(domainID, id, req)
			},
		}
		corrections = append(corrections, corr)
	}

	return corrections, nil
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *cloudnsProvider) GetZoneRecords(domain string) (models.Records, error) {
	records, err := c.getRecords(domain)
	if err != nil {
		return nil, err
	}
	existingRecords := make([]*models.RecordConfig, len(records))
	for i := range records {
		existingRecords[i] = toRc(domain, &records[i])
	}
	return existingRecords, nil
}

// EnsureDomainExists returns an error if domain doesn't exist.
func (c *cloudnsProvider) EnsureDomainExists(domain string) error {
	if err := c.fetchDomainList(); err != nil {
		return err
	}
	// domain already exists
	if _, ok := c.domainIndex[domain]; ok {
		return nil
	}
	return c.createDomain(domain)
}

//parses the ClouDNS format into our standard RecordConfig
func toRc(domain string, r *domainRecord) *models.RecordConfig {

	ttl, _ := strconv.ParseUint(r.TTL, 10, 32)
	priority, _ := strconv.ParseUint(r.Priority, 10, 32)
	weight, _ := strconv.ParseUint(r.Weight, 10, 32)
	port, _ := strconv.ParseUint(r.Port, 10, 32)

	rc := &models.RecordConfig{
		Type:         r.Type,
		TTL:          uint32(ttl),
		MxPreference: uint16(priority),
		SrvPriority:  uint16(priority),
		SrvWeight:    uint16(weight),
		SrvPort:      uint16(port),
		Original:     r,
	}
	rc.SetLabel(r.Host, domain)

	switch rtype := r.Type; rtype { // #rtype_variations
	case "TXT":
		rc.SetTargetTXT(r.Target)
	case "CNAME", "MX", "NS", "SRV", "ALIAS", "PTR":
		rc.SetTarget(dnsutil.AddOrigin(r.Target+".", domain))
	case "CAA":
		caaFlag, _ := strconv.ParseUint(r.CaaFlag, 10, 32)
		rc.CaaFlag = uint8(caaFlag)
		rc.CaaTag = r.CaaTag
		rc.SetTarget(r.CaaValue)
	case "TLSA":
		tlsaUsage, _ := strconv.ParseUint(r.TlsaUsage, 10, 32)
		rc.TlsaUsage = uint8(tlsaUsage)
		tlsaSelector, _ := strconv.ParseUint(r.TlsaSelector, 10, 32)
		rc.TlsaSelector = uint8(tlsaSelector)
		tlsaMatchingType, _ := strconv.ParseUint(r.TlsaMatchingType, 10, 32)
		rc.TlsaMatchingType = uint8(tlsaMatchingType)
		rc.SetTarget(r.Target)
	case "SSHFP":
		sshfpAlgorithm, _ := strconv.ParseUint(r.SshfpAlgorithm, 10, 32)
		rc.SshfpAlgorithm = uint8(sshfpAlgorithm)
		sshfpFingerprint, _ := strconv.ParseUint(r.SshfpFingerprint, 10, 32)
		rc.SshfpFingerprint = uint8(sshfpFingerprint)
		rc.SetTarget(r.Target)
	case "DS":
		dsKeyTag, _ := strconv.ParseUint(r.DsKeyTag, 10, 32)
		rc.DsKeyTag = uint16(dsKeyTag)
		dsAlgorithm, _ := strconv.ParseUint(r.SshfpAlgorithm, 10, 32) // SshFpAlgorithm and DsAlgorithm both use json field "algorithm"
		rc.DsAlgorithm = uint8(dsAlgorithm)
		dsDigestType, _ := strconv.ParseUint(r.DsDigestType, 10, 32)
		rc.DsDigestType = uint8(dsDigestType)
		rc.DsDigest = r.Target
		rc.SetTarget(r.Target)
	default:
		rc.SetTarget(r.Target)
	}

	return rc
}

//toReq takes a RecordConfig and turns it into the native format used by the API.
func toReq(rc *models.RecordConfig) (requestParams, error) {
	req := requestParams{
		"record-type": rc.Type,
		"host":        rc.GetLabel(),
		"record":      rc.GetTargetField(),
		"ttl":         strconv.Itoa(int(rc.TTL)),
	}

	// ClouDNS doesn't use "@", it uses an empty name
	if req["host"] == "@" {
		req["host"] = ""
	}

	switch rc.Type { // #rtype_variations
	case "A", "AAAA", "NS", "PTR", "TXT", "SOA", "ALIAS", "CNAME":
		// Nothing special.
	case "MX":
		req["priority"] = strconv.Itoa(int(rc.MxPreference))
	case "SRV":
		req["priority"] = strconv.Itoa(int(rc.SrvPriority))
		req["weight"] = strconv.Itoa(int(rc.SrvWeight))
		req["port"] = strconv.Itoa(int(rc.SrvPort))
	case "CAA":
		req["caa_flag"] = strconv.Itoa(int(rc.CaaFlag))
		req["caa_type"] = rc.CaaTag
		req["caa_value"] = rc.GetTargetField()
	case "TLSA":
		req["tlsa_usage"] = strconv.Itoa(int(rc.TlsaUsage))
		req["tlsa_selector"] = strconv.Itoa(int(rc.TlsaSelector))
		req["tlsa_matching_type"] = strconv.Itoa(int(rc.TlsaMatchingType))
	case "SSHFP":
		req["algorithm"] = strconv.Itoa(int(rc.SshfpAlgorithm))
		req["fptype"] = strconv.Itoa(int(rc.SshfpFingerprint))
	case "DS":
		req["key-tag"] = strconv.Itoa(int(rc.DsKeyTag))
		req["algorithm"] = strconv.Itoa(int(rc.DsAlgorithm))
		req["digest-type"] = strconv.Itoa(int(rc.DsDigestType))
		req["record"] = rc.DsDigest
	default:
		return nil, fmt.Errorf("ClouDNS.toReq rtype %q unimplemented", rc.Type)
	}

	return req, nil
}
<commit_msg>ClouDNS does not support changing the NAPTR in the API. (#1159)<commit_after>package cloudns

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/miekg/dns/dnsutil"

	"github.com/StackExchange/dnscontrol/v3/models"
	"github.com/StackExchange/dnscontrol/v3/pkg/diff"
	"github.com/StackExchange/dnscontrol/v3/providers"
)

/*
ClouDNS API DNS provider:
Info required in `creds.json`:
   - auth-id or sub-auth-id
   - auth-password
*/

// NewCloudns creates the provider.
func NewCloudns(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	c := &cloudnsProvider{}

	c.creds.id, c.creds.password, c.creds.subid = m["auth-id"], m["auth-password"], m["sub-auth-id"]

	if (c.creds.id == "" && c.creds.subid == "") || c.creds.password == "" {
		return nil, fmt.Errorf("missing ClouDNS auth-id or sub-auth-id and auth-password")
	}

	// Get a domain to validate authentication
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}

	return c, nil
}

var features = providers.DocumentationNotes{
	providers.DocDualHost:            providers.Unimplemented(),
	providers.DocOfficiallySupported: providers.Cannot(),
	providers.DocCreateDomains:       providers.Can(),
	providers.CanUseAlias:            providers.Can(),
	providers.CanUseSRV:              providers.Can(),
	providers.CanUseSSHFP:            providers.Can(),
	providers.CanUseCAA:              providers.Can(),
	providers.CanUseTLSA:             providers.Can(),
	providers.CanUsePTR:              providers.Can(),
	providers.CanGetZones:            providers.Can(),
	providers.CanUseDSForChildren:    providers.Can(),
	//providers.CanUseDS:               providers.Can(),  // in ClouDNS we can add  DS record just for a subdomain(child)
}

func init() {
	fns := providers.DspFuncs{
		Initializer:   NewCloudns,
		RecordAuditor: AuditRecords,
	}
	providers.RegisterDomainServiceProviderType("CLOUDNS", fns, features)
}

// GetNameservers returns the nameservers for a domain.
func (c *cloudnsProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
	if len(c.nameserversNames) == 0 {
		c.fetchAvailableNameservers()
	}
	return models.ToNameservers(c.nameserversNames)
}

// GetDomainCorrections returns the corrections for a domain.
func (c *cloudnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	dc, err := dc.Copy()
	if err != nil {
		return nil, err
	}

	dc.Punycode()

	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	domainID, ok := c.domainIndex[dc.Name]
	if !ok {
		return nil, fmt.Errorf("'%s' not a zone in ClouDNS account", dc.Name)
	}

	existingRecords, err := c.GetZoneRecords(dc.Name)
	if err != nil {
		return nil, err
	}
	// Normalize
	models.PostProcessRecords(existingRecords)

	// Get a list of available TTL values.
	// The TTL list needs to be obtained for each domain, so get it first here.
	c.fetchAvailableTTLValues(dc.Name)
	// ClouDNS can only be specified from a specific TTL list, so change the TTL in advance.
	for _, record := range dc.Records {
		record.TTL = fixTTL(record.TTL)
	}

	differ := diff.New(dc)
	_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
	if err != nil {
		return nil, err
	}

	var corrections []*models.Correction

	// Deletes first so changing type works etc.
	for _, m := range del {
		id := m.Existing.Original.(*domainRecord).ID
		corr := &models.Correction{
			Msg: fmt.Sprintf("%s, ClouDNS ID: %s", m.String(), id),
			F: func() error {
				return c.deleteRecord(domainID, id)
			},
		}
		// at ClouDNS, we MUST have a NS for a DS
		// So, when deleting, we must delete the DS first, otherwise deleting the NS throws an error
		if m.Existing.Type == "DS" {
			// type DS is prepended - so executed first
			corrections = append([]*models.Correction{corr}, corrections...)
		} else {
			corrections = append(corrections, corr)
		}
	}

	var createCorrections []*models.Correction
	for _, m := range create {
		req, err := toReq(m.Desired)
		if err != nil {
			return nil, err
		}

		corr := &models.Correction{
			Msg: m.String(),
			F: func() error {
				return c.createRecord(domainID, req)
			},
		}
		// at ClouDNS, we MUST have a NS for a DS
		// So, when creating, we must create the NS first, otherwise creating the DS throws an error
		if m.Desired.Type == "NS" {
			// type NS is prepended - so executed first
			createCorrections = append([]*models.Correction{corr}, createCorrections...)
		} else {
			createCorrections = append(createCorrections, corr)
		}
	}
	corrections = append(corrections, createCorrections...)

	for _, m := range modify {
		id := m.Existing.Original.(*domainRecord).ID
		req, err := toReq(m.Desired)
		if err != nil {
			return nil, err
		}

		corr := &models.Correction{
			Msg: fmt.Sprintf("%s, ClouDNS ID: %s: ", m.String(), id),
			F: func() error {
				return c.modifyRecord(domainID, id, req)
			},
		}
		corrections = append(corrections, corr)
	}

	return corrections, nil
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *cloudnsProvider) GetZoneRecords(domain string) (models.Records, error) {
	records, err := c.getRecords(domain)
	if err != nil {
		return nil, err
	}
	existingRecords := make([]*models.RecordConfig, len(records))
	for i := range records {
		existingRecords[i] = toRc(domain, &records[i])
	}
	return existingRecords, nil
}

// EnsureDomainExists returns an error if domain doesn't exist.
func (c *cloudnsProvider) EnsureDomainExists(domain string) error {
	if err := c.fetchDomainList(); err != nil {
		return err
	}
	// domain already exists
	if _, ok := c.domainIndex[domain]; ok {
		return nil
	}
	return c.createDomain(domain)
}

//parses the ClouDNS format into our standard RecordConfig
func toRc(domain string, r *domainRecord) *models.RecordConfig {

	ttl, _ := strconv.ParseUint(r.TTL, 10, 32)
	priority, _ := strconv.ParseUint(r.Priority, 10, 32)
	weight, _ := strconv.ParseUint(r.Weight, 10, 32)
	port, _ := strconv.ParseUint(r.Port, 10, 32)

	rc := &models.RecordConfig{
		Type:         r.Type,
		TTL:          uint32(ttl),
		MxPreference: uint16(priority),
		SrvPriority:  uint16(priority),
		SrvWeight:    uint16(weight),
		SrvPort:      uint16(port),
		Original:     r,
	}
	rc.SetLabel(r.Host, domain)

	switch rtype := r.Type; rtype { // #rtype_variations
	case "TXT":
		rc.SetTargetTXT(r.Target)
	case "CNAME", "MX", "NS", "SRV", "ALIAS", "PTR":
		rc.SetTarget(dnsutil.AddOrigin(r.Target+".", domain))
	case "CAA":
		caaFlag, _ := strconv.ParseUint(r.CaaFlag, 10, 32)
		rc.CaaFlag = uint8(caaFlag)
		rc.CaaTag = r.CaaTag
		rc.SetTarget(r.CaaValue)
	case "TLSA":
		tlsaUsage, _ := strconv.ParseUint(r.TlsaUsage, 10, 32)
		rc.TlsaUsage = uint8(tlsaUsage)
		tlsaSelector, _ := strconv.ParseUint(r.TlsaSelector, 10, 32)
		rc.TlsaSelector = uint8(tlsaSelector)
		tlsaMatchingType, _ := strconv.ParseUint(r.TlsaMatchingType, 10, 32)
		rc.TlsaMatchingType = uint8(tlsaMatchingType)
		rc.SetTarget(r.Target)
	case "SSHFP":
		sshfpAlgorithm, _ := strconv.ParseUint(r.SshfpAlgorithm, 10, 32)
		rc.SshfpAlgorithm = uint8(sshfpAlgorithm)
		sshfpFingerprint, _ := strconv.ParseUint(r.SshfpFingerprint, 10, 32)
		rc.SshfpFingerprint = uint8(sshfpFingerprint)
		rc.SetTarget(r.Target)
	case "DS":
		dsKeyTag, _ := strconv.ParseUint(r.DsKeyTag, 10, 32)
		rc.DsKeyTag = uint16(dsKeyTag)
		dsAlgorithm, _ := strconv.ParseUint(r.SshfpAlgorithm, 10, 32) // SshFpAlgorithm and DsAlgorithm both use json field "algorithm"
		rc.DsAlgorithm = uint8(dsAlgorithm)
		dsDigestType, _ := strconv.ParseUint(r.DsDigestType, 10, 32)
		rc.DsDigestType = uint8(dsDigestType)
		rc.DsDigest = r.Target
		rc.SetTarget(r.Target)
	default:
		rc.SetTarget(r.Target)
	}

	return rc
}

//toReq takes a RecordConfig and turns it into the native format used by the API.
func toReq(rc *models.RecordConfig) (requestParams, error) {
	req := requestParams{
		"record-type": rc.Type,
		"host":        rc.GetLabel(),
		"record":      rc.GetTargetField(),
		"ttl":         strconv.Itoa(int(rc.TTL)),
	}

	// ClouDNS doesn't use "@", it uses an empty name
	if req["host"] == "@" {
		req["host"] = ""
	}

	switch rc.Type { // #rtype_variations
	case "A", "AAAA", "NS", "PTR", "TXT", "SOA", "ALIAS", "CNAME":
		// Nothing special.
	case "MX":
		req["priority"] = strconv.Itoa(int(rc.MxPreference))
	case "SRV":
		req["priority"] = strconv.Itoa(int(rc.SrvPriority))
		req["weight"] = strconv.Itoa(int(rc.SrvWeight))
		req["port"] = strconv.Itoa(int(rc.SrvPort))
	case "CAA":
		req["caa_flag"] = strconv.Itoa(int(rc.CaaFlag))
		req["caa_type"] = rc.CaaTag
		req["caa_value"] = rc.GetTargetField()
	case "TLSA":
		req["tlsa_usage"] = strconv.Itoa(int(rc.TlsaUsage))
		req["tlsa_selector"] = strconv.Itoa(int(rc.TlsaSelector))
		req["tlsa_matching_type"] = strconv.Itoa(int(rc.TlsaMatchingType))
	case "SSHFP":
		req["algorithm"] = strconv.Itoa(int(rc.SshfpAlgorithm))
		req["fptype"] = strconv.Itoa(int(rc.SshfpFingerprint))
	case "DS":
		req["key-tag"] = strconv.Itoa(int(rc.DsKeyTag))
		req["algorithm"] = strconv.Itoa(int(rc.DsAlgorithm))
		req["digest-type"] = strconv.Itoa(int(rc.DsDigestType))
		req["record"] = rc.DsDigest
	default:
		return nil, fmt.Errorf("ClouDNS.toReq rtype %q unimplemented", rc.Type)
	}

	return req, nil
}
<|endoftext|>