<commit_before>package rest

import (
	"net/http"

	"github.com/leanovate/microtools/routing"
)

type HTTPError struct {
	Code    int    `json:"code" xml:"code"`
	Type    string `json:"type" xml:"type"`
	Message string `json:"message" xml:"message"`
	Details string `json:"details,omitempty" xml:"details,omitempty"`
}

func (e *HTTPError) Error() string {
	return e.Details
}

func (e *HTTPError) Send(response http.ResponseWriter, encoder ResponseEncoder) {
	response.WriteHeader(e.Code)
	if err := encoder.Encode(response, e); err != nil {
		response.Write([]byte(e.Message))
	}
}

func HttpErrorMatcher(httpError *HTTPError) routing.Matcher {
	return func(remainingPath string, resp http.ResponseWriter, req *http.Request) bool {
		encoder := StdResponseEncoderChooser(req)
		httpError.Send(resp, encoder)
		return true
	}
}

func WrapError(err error) *HTTPError {
	switch err.(type) {
	case *HTTPError:
		return err.(*HTTPError)
	default:
		return InternalServerError(err)
	}
}

var BadRequest = &HTTPError{
	Code:    400,
	Type:    "https://httpstatus.es/400",
	Message: "Bad request",
}

var UnauthorizedError = &HTTPError{
	Code:    401,
	Type:    "https://httpstatus.es/401",
	Message: "Unauthorized",
}

var Forbidden = &HTTPError{
	Code:    403,
	Type:    "https://httpstatus.es/403",
	Message: "Forbidden",
}

var NotFound = &HTTPError{
	Code:    404,
	Type:    "https://httpstatus.es/404",
	Message: "Not found",
}

var MethodNotAllowed = &HTTPError{
	Code:    405,
	Type:    "https://httpstatus.es/405",
	Message: "Method not allowed",
}

func InternalServerError(err error) *HTTPError {
	return &HTTPError{
		Code:    500,
		Type:    "https://httpstatus.es/500",
		Message: "InternalServerError",
		Details: err.Error(),
	}
}
<commit_msg>Slighty improve http error<commit_after>package rest

import (
	"net/http"

	"github.com/leanovate/microtools/routing"
)

type HTTPError struct {
	Code    int    `json:"code" xml:"code"`
	Type    string `json:"type" xml:"type"`
	Message string `json:"message" xml:"message"`
	Details string `json:"details,omitempty" xml:"details,omitempty"`
}

func (e *HTTPError) Error() string {
	return e.Details
}

func (e *HTTPError) Send(response http.ResponseWriter, encoder ResponseEncoder) {
	response.WriteHeader(e.Code)
	if err := encoder.Encode(response, e); err != nil {
		response.Write([]byte(e.Message))
	}
}

func (e *HTTPError) WithDetails(details string) *HTTPError {
	return &HTTPError{
		Code:    e.Code,
		Type:    e.Type,
		Message: e.Message,
		Details: details,
	}
}

func HttpErrorMatcher(httpError *HTTPError) routing.Matcher {
	return func(remainingPath string, resp http.ResponseWriter, req *http.Request) bool {
		encoder := StdResponseEncoderChooser(req)
		httpError.Send(resp, encoder)
		return true
	}
}

func WrapError(err error) *HTTPError {
	switch err.(type) {
	case *HTTPError:
		return err.(*HTTPError)
	default:
		return InternalServerError(err)
	}
}

var BadRequest = &HTTPError{
	Code:    400,
	Type:    "https://httpstatus.es/400",
	Message: "Bad request",
}

var UnauthorizedError = &HTTPError{
	Code:    401,
	Type:    "https://httpstatus.es/401",
	Message: "Unauthorized",
}

var Forbidden = &HTTPError{
	Code:    403,
	Type:    "https://httpstatus.es/403",
	Message: "Forbidden",
}

var NotFound = &HTTPError{
	Code:    404,
	Type:    "https://httpstatus.es/404",
	Message: "Not found",
}

var MethodNotAllowed = &HTTPError{
	Code:    405,
	Type:    "https://httpstatus.es/405",
	Message: "Method not allowed",
}

var Conflict = &HTTPError{
	Code:    409,
	Type:    "https://httpstatus.es/409",
	Message: "Conflict",
}

func InternalServerError(err error) *HTTPError {
	return &HTTPError{
		Code:    500,
		Type:    "https://httpstatus.es/500",
		Message: "InternalServerError",
		Details: err.Error(),
	}
}
<|endoftext|>


<commit_before>package controllers_test

import (
	"github.com/yang-f/beauty/db"
	"github.com/yang-f/beauty/router"
	"github.com/yang-f/beauty/settings"
	"net/http"
	"net/http/httptest"
	"testing"
)

func init() {
	db.Query("drop database beauty_test")
	db.Query("create database beauty_test")
	settings.Local["mysql_database"] = "beauty_test"
	db.Query(`create table if not exists user
        (
            user_id int primary key not null  auto_increment,
            user_name varchar(64),
            user_pass varchar(64),
            user_mobile varchar(32),
            user_type enum('user', 'admin', 'test') not null,
            add_time timestamp not null default CURRENT_TIMESTAMP
        )`)
	db.Query(`insert into user (user_name, user_pass, user_mobile, add_time) values('test', 'test', '12345678901', '2017-01-12 12:15:38')`)
}

func TestConfig(t *testing.T) {
	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()

	router := router.NewRouter()
	router.ServeHTTP(rr, req)
	if status := rr.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}
	expected := `{"description":"this is json"}`
	if rr.Body.String() != expected {
		t.Errorf("handler returned unexpected body: got %v want %v",
			rr.Body.String(), expected)
	}

}

func Benchmark_config(b *testing.B) {
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("GET", "/", nil)
		if err != nil {
			b.Fatal(err)
		}
		rr := httptest.NewRecorder()

		router := router.NewRouter()
		router.ServeHTTP(rr, req)
		if status := rr.Code; status != http.StatusOK {
			b.Errorf("handler returned wrong status code: got %v want %v",
				status, http.StatusOK)
		}
		expected := `{"description":"this is json"}`
		if rr.Body.String() != expected {
			b.Errorf("handler returned unexpected body: got %v want %v",
				rr.Body.String(), expected)
		}
	}
}
<commit_msg>controller test<commit_after>package controllers_test

import (
	"github.com/yang-f/beauty/db"
	"github.com/yang-f/beauty/router"
	"github.com/yang-f/beauty/settings"
	"net/http"
	"net/http/httptest"
	"testing"
)

func init() {
	db.Query("drop database beauty_test")
	db.Query("create database beauty_test")
	settings.Local["mysql_database"] = "beauty_test"
	db.Query(`create table if not exists user
        (
            user_id int primary key not null  auto_increment,
            user_name varchar(64),
            user_pass varchar(64),
            user_mobile varchar(32),
            user_type enum('user', 'admin', 'test') not null,
            add_time timestamp not null default CURRENT_TIMESTAMP
        )`)
	db.Query(`insert into user (user_name, user_pass, user_mobile, add_time) values('test', 'test', '12345678901', '2017-01-12 12:15:38')`)
}

type tester struct {
	Pattern  string
	Status   int
	Expected string
	Method   string
}

func TestConfig(t *testing.T) {
	var tests = []tester{
		{"/", 200, `{"description":"this is json"}`, "GET"},
		{"/demo1", 403, `{"status":403,"description":"token not found.","code":"AUTH_FAILED"}`, "GET"},
		{"/demo2", 200, `{"description":"this is json"}`, "GET"},
		{"/demo3", 403, `{"status":403,"description":"token not found.","code":"AUTH_FAILED"}`, "GET"},
	}

	for _, test := range tests {
		req, err := http.NewRequest(test.Method, test.Pattern, nil)
		if err != nil {
			t.Fatal(err)
		}
		rr := httptest.NewRecorder()

		router := router.NewRouter()
		router.ServeHTTP(rr, req)
		if status := rr.Code; status != test.Status {
			t.Errorf("handler returned wrong status code: got %v want %v",
				status, http.StatusOK)
		}
		if rr.Body.String() != test.Expected {
			t.Errorf("handler returned unexpected body: got %v want %v",
				rr.Body.String(), test.Expected)
		}
	}
}

func Benchmark_config(b *testing.B) {
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("GET", "/", nil)
		if err != nil {
			b.Fatal(err)
		}
		rr := httptest.NewRecorder()

		router := router.NewRouter()
		router.ServeHTTP(rr, req)
		if status := rr.Code; status != http.StatusOK {
			b.Errorf("handler returned wrong status code: got %v want %v",
				status, http.StatusOK)
		}
		expected := `{"description":"this is json"}`
		if rr.Body.String() != expected {
			b.Errorf("handler returned unexpected body: got %v want %v",
				rr.Body.String(), expected)
		}
	}
}
<|endoftext|>


<commit_before>package rockredis

import (
	"errors"
)

const (
	EngType = "rockredis"
)

type ErrType int64

const (
	NotFound ErrType = 1
)

// for backend store
const (
	NoneType byte = 0
	// 0~10 reserved for system usage

	// table count, stats, index, schema, and etc.
	TableMetaType byte = 10

	// for data
	KVType    byte = 21
	HashType  byte = 22
	HSizeType byte = 23
	// current using array list
	ListType   byte = 24
	LMetaType  byte = 25
	ZSetType   byte = 26
	ZSizeType  byte = 27
	ZScoreType byte = 28
	SetType    byte = 29
	SSizeType  byte = 30

	JSONType byte = 31

	// this type has a custom partition key length
	// to allow all the data store in the same partition
	// this type allow the transaction in the same tx group,
	// which will be stored in the same partition
	FixPartType byte = 50
	maxDataType byte = 100

	// use the exp table to store all the expire time for the key
	// and scan periodically to delete the expired keys
	ExpTimeType byte = 101
	ExpMetaType byte = 102
)

var (
	TypeName = map[byte]string{
		KVType:     "kv",
		HashType:   "hash",
		HSizeType:  "hsize",
		ListType:   "list",
		LMetaType:  "lmeta",
		ZSetType:   "zset",
		ZSizeType:  "zsize",
		ZScoreType: "zscore",
		SetType:    "set",
		SSizeType:  "ssize",
	}
)

const (
	defaultScanCount int = 100
	MAX_BATCH_NUM        = 5000
	RANGE_DELETE_NUM     = 100000
)

var (
	errKeySize          = errors.New("invalid key size")
	errValueSize        = errors.New("invalid value size")
	errZSetMemberSize   = errors.New("invalid zset member size")
	errTooMuchBatchSize = errors.New("the batch size exceed the limit")
)

const (
	MaxDatabases int = 10240

	MaxTableNameLen int = 255
	MaxColumnLen    int = 255
	//max key size
	MaxKeySize int = 10240

	//max hash field size
	MaxHashFieldSize int = 10240

	//max zset member size
	MaxZSetMemberSize int = 10240

	//max set member size
	MaxSetMemberSize int = 10240

	//max value size
	MaxValueSize int = 1024 * 1024 * 8
)

var (
	ErrZScoreMiss   = errors.New("zset score miss")
	ErrWriteInROnly = errors.New("write not support in readonly mode")
)
<commit_msg>add index type for rockredis<commit_after>package rockredis

import (
	"errors"
)

const (
	EngType = "rockredis"
)

type ErrType int64

const (
	NotFound ErrType = 1
)

// for backend store
const (
	NoneType byte = 0
	// 0~10 reserved for system usage

	// table count, stats, index, schema, and etc.
	TableMetaType      byte = 10
	TableIndexMetaType byte = 11

	// for data
	KVType    byte = 21
	HashType  byte = 22
	HSizeType byte = 23
	// current using array list
	ListType   byte = 24
	LMetaType  byte = 25
	ZSetType   byte = 26
	ZSizeType  byte = 27
	ZScoreType byte = 28
	SetType    byte = 29
	SSizeType  byte = 30

	JSONType    byte = 31
	HLLType     byte = 32 // hyperloglog
	GeoHashType byte = 33

	ColumnType byte = 38 // used for column store for OLAP

	NonUniqueIndex byte = 40
	UniqueIndex    byte = 41

	FullTextInvertedIndex   byte = 50
	FullTextRoaringmapIndex byte = 51
	// this type has a custom partition key length
	// to allow all the data store in the same partition
	// this type allow the transaction in the same tx group,
	// which will be stored in the same partition
	FixPartType byte = 80
	maxDataType byte = 100

	// use the exp table to store all the expire time for the key
	// and scan periodically to delete the expired keys
	ExpTimeType byte = 101
	ExpMetaType byte = 102
)

var (
	TypeName = map[byte]string{
		KVType:     "kv",
		HashType:   "hash",
		HSizeType:  "hsize",
		ListType:   "list",
		LMetaType:  "lmeta",
		ZSetType:   "zset",
		ZSizeType:  "zsize",
		ZScoreType: "zscore",
		SetType:    "set",
		SSizeType:  "ssize",
	}
)

const (
	defaultScanCount int = 100
	MAX_BATCH_NUM        = 5000
	RANGE_DELETE_NUM     = 100000
)

var (
	errKeySize          = errors.New("invalid key size")
	errValueSize        = errors.New("invalid value size")
	errZSetMemberSize   = errors.New("invalid zset member size")
	errTooMuchBatchSize = errors.New("the batch size exceed the limit")
)

const (
	MaxDatabases int = 10240

	MaxTableNameLen int = 255
	MaxColumnLen    int = 255
	//max key size
	MaxKeySize int = 10240

	//max hash field size
	MaxHashFieldSize int = 10240

	//max zset member size
	MaxZSetMemberSize int = 10240

	//max set member size
	MaxSetMemberSize int = 10240

	//max value size
	MaxValueSize int = 1024 * 1024 * 8
)

var (
	ErrZScoreMiss   = errors.New("zset score miss")
	ErrWriteInROnly = errors.New("write not support in readonly mode")
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"time"
)

type SOACheck struct {
	NS  []NSData
	SOA []SOAData
	Report
}

type SOAData struct {
	Name  string
	IP    string
	SOA   *dns.SOA
	Error string
}

func (c *SOACheck) Scan(domain string) {
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := SOAData{Name: ns.Name, IP: nsip.String()}
			soa, _, err := queryRRset(domain, dns.TypeSOA, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), err)
				c.SOA = append(c.SOA, data)
				break
			}
			if len(soa) == 0 {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), "no records found")
				c.SOA = append(c.SOA, data)
				break
			}
			data.SOA = soa[0].(*dns.SOA)
			c.SOA = append(c.SOA, data)
		}
	}
}

func (c *SOACheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			m[ns.SOA.String()] = append(m[ns.SOA.String()], ns.Name+"("+ns.IP+")")
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: SOA not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : SOA of all nameservers are identical"
		res.Status = true
	}
	return res
}

func checkSerial(serial uint32) bool {
	serialstr := fmt.Sprintf("%v", serial)
	if len(serialstr) != 10 {
		return false
	}
	_, err := time.Parse("20060102", serialstr[:len(serialstr)-2])
	if err != nil {
		return false
	}
	return true
}

func (c *SOACheck) checkMname(mname string) bool {
	for _, ns := range c.NS {
		if mname == dns.Fqdn(ns.Name) {
			return true
		}
	}
	return false
}

func (c *SOACheck) checkRFC1918() bool {
	for _, ns := range c.NS {
		for _, ip := range ns.IP {
			if isRFC1918(ip) {
				return true
			}
		}
	}
	return false
}

func (c *SOACheck) Values() []ReportResult {
	var soa *dns.SOA
	var results []ReportResult
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			soa = ns.SOA
		}
	}
	if checkSerial(soa.Serial) {
		results = append(results, ReportResult{Result: "OK  : Serial format appears to be in the recommended format of YYYYMMDDnn.",
			Status: true, Records: []string{soa.String()}})
	} else {
		results = append(results, ReportResult{Result: "WARN: Serial is not in the recommended format of YYYYMMDDnn.",
			Status: false})
	}
	if c.checkMname(soa.Ns) {
		results = append(results, ReportResult{Result: fmt.Sprintf("OK  : MNAME %s is listed at the parent servers.", soa.Ns),
			Status: true})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: MNAME %s is not listed at the parent servers.", soa.Ns),
			Status: false})
	}
	if !c.checkRFC1918() {
		results = append(results, ReportResult{Result: "OK  : Your nameservers have public / routable addresses.",
			Status: true})
	} else {
		results = append(results, ReportResult{Result: "FAIL: Some of your nameservers have non-routable (RFC1918) addresses.",
			Status: false})
	}
	return results
}

func (c *SOACheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "SOA"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
}
<commit_msg>Fix mname check (SOAcheck)<commit_after>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"time"
)

type SOACheck struct {
	NS     []NSData
	SOA    []SOAData
	Domain string
	Report
}

type SOAData struct {
	Name  string
	IP    string
	SOA   *dns.SOA
	Error string
}

func (c *SOACheck) Scan(domain string) {
	c.Domain = domain
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := SOAData{Name: ns.Name, IP: nsip.String()}
			soa, _, err := queryRRset(domain, dns.TypeSOA, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), err)
				c.SOA = append(c.SOA, data)
				break
			}
			if len(soa) == 0 {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), "no records found")
				c.SOA = append(c.SOA, data)
				break
			}
			data.SOA = soa[0].(*dns.SOA)
			c.SOA = append(c.SOA, data)
		}
	}
}

func (c *SOACheck) checkMname(mname string) bool {
	nsdata, err := findNS(getParentDomain(c.Domain))
	if err != nil {
		return false
	}
	var rrset []dns.RR
loop:
	for _, ns := range nsdata {
		for _, nsip := range ns.IP {
			res, err := query(dns.Fqdn(c.Domain), dns.TypeNS, nsip.String(), true)
			if err != nil {
				break
			}
			rrset = extractRR(res.Msg.Ns, dns.TypeNS)
			if len(rrset) > 0 {
				break loop
			}
		}
	}
	for _, pns := range rrset {
		if pns.(*dns.NS).Ns == mname {
			return true
		}
	}
	return false

}

func (c *SOACheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			m[ns.SOA.String()] = append(m[ns.SOA.String()], ns.Name+"("+ns.IP+")")
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: SOA not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : SOA of all nameservers are identical"
		res.Status = true
	}
	return res
}

func checkSerial(serial uint32) bool {
	serialstr := fmt.Sprintf("%v", serial)
	if len(serialstr) != 10 {
		return false
	}
	_, err := time.Parse("20060102", serialstr[:len(serialstr)-2])
	if err != nil {
		return false
	}
	return true
}

func (c *SOACheck) checkRFC1918() bool {
	for _, ns := range c.NS {
		for _, ip := range ns.IP {
			if isRFC1918(ip) {
				return true
			}
		}
	}
	return false
}

func (c *SOACheck) Values() []ReportResult {
	var soa *dns.SOA
	var results []ReportResult
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			soa = ns.SOA
		}
	}
	if checkSerial(soa.Serial) {
		results = append(results, ReportResult{Result: "OK  : Serial format appears to be in the recommended format of YYYYMMDDnn.",
			Status: true, Records: []string{soa.String()}})
	} else {
		results = append(results, ReportResult{Result: "WARN: Serial is not in the recommended format of YYYYMMDDnn.",
			Status: false})
	}
	if c.checkMname(soa.Ns) {
		results = append(results, ReportResult{Result: fmt.Sprintf("OK  : MNAME %s is listed at the parent servers.", soa.Ns),
			Status: true})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: MNAME %s is not listed at the parent servers.", soa.Ns),
			Status: false})
	}
	if !c.checkRFC1918() {
		results = append(results, ReportResult{Result: "OK  : Your nameservers have public / routable addresses.",
			Status: true})
	} else {
		results = append(results, ReportResult{Result: "FAIL: Some of your nameservers have non-routable (RFC1918) addresses.",
			Status: false})
	}
	return results
}

func (c *SOACheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "SOA"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
}
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"net"
	"strings"

	"github.com/Sirupsen/logrus"
	clustertypes "github.com/docker/docker/daemon/cluster/provider"
	"github.com/docker/docker/errors"
	"github.com/docker/docker/runconfig"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/network"
	"github.com/docker/libnetwork"
	networktypes "github.com/docker/libnetwork/types"
)

// NetworkControllerEnabled checks if the networking stack is enabled.
// This feature depends on OS primitives and it's disabled in systems like Windows.
func (daemon *Daemon) NetworkControllerEnabled() bool {
	return daemon.netController != nil
}

// FindNetwork function finds a network for a given string that can represent network name or id
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
	// Find by Name
	n, err := daemon.GetNetworkByName(idName)
	if err != nil && !isNoSuchNetworkError(err) {
		return nil, err
	}

	if n != nil {
		return n, nil
	}

	// Find by id
	return daemon.GetNetworkByID(idName)
}

func isNoSuchNetworkError(err error) bool {
	_, ok := err.(libnetwork.ErrNoSuchNetwork)
	return ok
}

// GetNetworkByID function returns a network whose ID begins with the given prefix.
// It fails with an error if no matching, or more than one matching, networks are found.
func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, error) {
	list := daemon.GetNetworksByID(partialID)

	if len(list) == 0 {
		return nil, libnetwork.ErrNoSuchNetwork(partialID)
	}
	if len(list) > 1 {
		return nil, libnetwork.ErrInvalidID(partialID)
	}
	return list[0], nil
}

// GetNetworkByName function returns a network for a given network name.
func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) {
	c := daemon.netController
	if c == nil {
		return nil, libnetwork.ErrNoSuchNetwork(name)
	}
	if name == "" {
		name = c.Config().Daemon.DefaultNetwork
	}
	return c.NetworkByName(name)
}

// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
	c := daemon.netController
	if c == nil {
		return nil
	}
	list := []libnetwork.Network{}
	l := func(nw libnetwork.Network) bool {
		if strings.HasPrefix(nw.ID(), partialID) {
			list = append(list, nw)
		}
		return false
	}
	c.WalkNetworks(l)

	return list
}

// getAllNetworks returns a list containing all networks
func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
	c := daemon.netController
	list := []libnetwork.Network{}
	l := func(nw libnetwork.Network) bool {
		list = append(list, nw)
		return false
	}
	c.WalkNetworks(l)

	return list
}

func isIngressNetwork(name string) bool {
	return name == "ingress"
}

var ingressChan = make(chan struct{}, 1)

func ingressWait() func() {
	ingressChan <- struct{}{}
	return func() { <-ingressChan }
}

// SetupIngress setups ingress networking.
func (daemon *Daemon) SetupIngress(create clustertypes.NetworkCreateRequest, nodeIP string) error {
	ip, _, err := net.ParseCIDR(nodeIP)
	if err != nil {
		return err
	}

	go func() {
		controller := daemon.netController
		controller.AgentInitWait()

		if n, err := daemon.GetNetworkByName(create.Name); err == nil && n != nil && n.ID() != create.ID {
			if err := controller.SandboxDestroy("ingress-sbox"); err != nil {
				logrus.Errorf("Failed to delete stale ingress sandbox: %v", err)
				return
			}

			if err := n.Delete(); err != nil {
				logrus.Errorf("Failed to delete stale ingress network %s: %v", n.ID(), err)
				return
			}
		}

		if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
			// If it is any other error other than already
			// exists error log error and return.
			if _, ok := err.(libnetwork.NetworkNameError); !ok {
				logrus.Errorf("Failed creating ingress network: %v", err)
				return
			}

			// Otherwise continue down the call to create or recreate sandbox.
		}

		n, err := daemon.GetNetworkByID(create.ID)
		if err != nil {
			logrus.Errorf("Failed getting ingress network by id after creating: %v", err)
			return
		}

		sb, err := controller.NewSandbox("ingress-sbox", libnetwork.OptionIngress())
		if err != nil {
			logrus.Errorf("Failed creating ingress sandbox: %v", err)
			return
		}

		ep, err := n.CreateEndpoint("ingress-endpoint", libnetwork.CreateOptionIpam(ip, nil, nil, nil))
		if err != nil {
			logrus.Errorf("Failed creating ingress endpoint: %v", err)
			return
		}

		if err := ep.Join(sb, nil); err != nil {
			logrus.Errorf("Failed joining ingress sandbox to ingress endpoint: %v", err)
		}
	}()

	return nil
}

// SetNetworkBootstrapKeys sets the bootstrap keys.
func (daemon *Daemon) SetNetworkBootstrapKeys(keys []*networktypes.EncryptionKey) error {
	return daemon.netController.SetKeys(keys)
}

// CreateManagedNetwork creates an agent network.
func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
	_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
	return err
}

// CreateNetwork creates a network with the given name, driver and other optional parameters
func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
	resp, err := daemon.createNetwork(create, "", false)
	if err != nil {
		return nil, err
	}
	return resp, err
}

func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
	// If there is a pending ingress network creation wait here
	// since ingress network creation can happen via node download
	// from manager or task download.
	if isIngressNetwork(create.Name) {
		defer ingressWait()()
	}

	if runconfig.IsPreDefinedNetwork(create.Name) && !agent {
		err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
		return nil, errors.NewRequestForbiddenError(err)
	}

	var warning string
	nw, err := daemon.GetNetworkByName(create.Name)
	if err != nil {
		if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
			return nil, err
		}
	}
	if nw != nil {
		if create.CheckDuplicate {
			return nil, libnetwork.NetworkNameError(create.Name)
		}
		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
	}

	c := daemon.netController
	driver := create.Driver
	if driver == "" {
		driver = c.Config().Daemon.DefaultDriver
	}

	ipam := create.IPAM
	v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
	if err != nil {
		return nil, err
	}

	nwOptions := []libnetwork.NetworkOption{
		libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options),
		libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
		libnetwork.NetworkOptionDriverOpts(create.Options),
		libnetwork.NetworkOptionLabels(create.Labels),
	}
	if create.Internal {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
	}
	if agent {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionDynamic())
		nwOptions = append(nwOptions, libnetwork.NetworkOptionPersist(false))
	}

	if isIngressNetwork(create.Name) {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionIngress())
	}

	n, err := c.NewNetwork(driver, create.Name, id, nwOptions...)
	if err != nil {
		return nil, err
	}

	daemon.LogNetworkEvent(n, "create")
	return &types.NetworkCreateResponse{
		ID:      n.ID(),
		Warning: warning,
	}, nil
}

func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) {
	ipamV4Cfg := []*libnetwork.IpamConf{}
	ipamV6Cfg := []*libnetwork.IpamConf{}
	for _, d := range data {
		iCfg := libnetwork.IpamConf{}
		iCfg.PreferredPool = d.Subnet
		iCfg.SubPool = d.IPRange
		iCfg.Gateway = d.Gateway
		iCfg.AuxAddresses = d.AuxAddress
		ip, _, err := net.ParseCIDR(d.Subnet)
		if err != nil {
			return nil, nil, fmt.Errorf("Invalid subnet %s : %v", d.Subnet, err)
		}
		if ip.To4() != nil {
			ipamV4Cfg = append(ipamV4Cfg, &iCfg)
		} else {
			ipamV6Cfg = append(ipamV6Cfg, &iCfg)
		}
	}
	return ipamV4Cfg, ipamV6Cfg, nil
}

// UpdateContainerServiceConfig updates a service configuration.
func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		return err
	}

	container.NetworkSettings.Service = serviceConfig
	return nil
}

// ConnectContainerToNetwork connects the given container to the given
// network. If either cannot be found, an err is returned. If the
// network cannot be set up, an err is returned.
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		return err
	}
	return daemon.ConnectToNetwork(container, networkName, endpointConfig)
}

// DisconnectContainerFromNetwork disconnects the given container from
// the given network. If either cannot be found, an err is returned.
func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		if force {
			return daemon.ForceEndpointDelete(containerName, network)
		}
		return err
	}
	return daemon.DisconnectFromNetwork(container, network, force)
}

// GetNetworkDriverList returns the list of plugins drivers
// registered for network.
func (daemon *Daemon) GetNetworkDriverList() map[string]bool {
	pluginList := make(map[string]bool)

	if !daemon.NetworkControllerEnabled() {
		return nil
	}
	c := daemon.netController
	networks := c.Networks()

	for _, network := range networks {
		driver := network.Type()
		pluginList[driver] = true
	}
	// TODO : Replace this with proper libnetwork API
	pluginList["overlay"] = true

	return pluginList
}

// DeleteManagedNetwork deletes an agent network.
func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
	return daemon.deleteNetwork(networkID, true)
}

// DeleteNetwork destroys a network unless it's one of docker's predefined networks.
func (daemon *Daemon) DeleteNetwork(networkID string) error {
	return daemon.deleteNetwork(networkID, false)
}

func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
	nw, err := daemon.FindNetwork(networkID)
	if err != nil {
		return err
	}

	if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
		err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
		return errors.NewRequestForbiddenError(err)
	}

	if err := nw.Delete(); err != nil {
		return err
	}
	daemon.LogNetworkEvent(nw, "destroy")
	return nil
}

// GetNetworks returns a list of all networks
func (daemon *Daemon) GetNetworks() []libnetwork.Network {
	return daemon.getAllNetworks()
}
<commit_msg>Fixing a stale endpoint issue that blocks ingress network cleanup<commit_after>package daemon

import (
	"fmt"
	"net"
	"strings"

	"github.com/Sirupsen/logrus"
	clustertypes "github.com/docker/docker/daemon/cluster/provider"
	"github.com/docker/docker/errors"
	"github.com/docker/docker/runconfig"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/network"
	"github.com/docker/libnetwork"
	networktypes "github.com/docker/libnetwork/types"
)

// NetworkControllerEnabled checks if the networking stack is enabled.
// This feature depends on OS primitives and it's disabled in systems like Windows.
func (daemon *Daemon) NetworkControllerEnabled() bool {
	return daemon.netController != nil
}

// FindNetwork function finds a network for a given string that can represent network name or id
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
	// Find by Name
	n, err := daemon.GetNetworkByName(idName)
	if err != nil && !isNoSuchNetworkError(err) {
		return nil, err
	}

	if n != nil {
		return n, nil
	}

	// Find by id
	return daemon.GetNetworkByID(idName)
}

func isNoSuchNetworkError(err error) bool {
	_, ok := err.(libnetwork.ErrNoSuchNetwork)
	return ok
}

// GetNetworkByID function returns a network whose ID begins with the given prefix.
// It fails with an error if no matching, or more than one matching, networks are found.
func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, error) {
	list := daemon.GetNetworksByID(partialID)

	if len(list) == 0 {
		return nil, libnetwork.ErrNoSuchNetwork(partialID)
	}
	if len(list) > 1 {
		return nil, libnetwork.ErrInvalidID(partialID)
	}
	return list[0], nil
}

// GetNetworkByName function returns a network for a given network name.
func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) {
	c := daemon.netController
	if c == nil {
		return nil, libnetwork.ErrNoSuchNetwork(name)
	}
	if name == "" {
		name = c.Config().Daemon.DefaultNetwork
	}
	return c.NetworkByName(name)
}

// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
	c := daemon.netController
	if c == nil {
		return nil
	}
	list := []libnetwork.Network{}
	l := func(nw libnetwork.Network) bool {
		if strings.HasPrefix(nw.ID(), partialID) {
			list = append(list, nw)
		}
		return false
	}
	c.WalkNetworks(l)

	return list
}

// getAllNetworks returns a list containing all networks
func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
	c := daemon.netController
	list := []libnetwork.Network{}
	l := func(nw libnetwork.Network) bool {
		list = append(list, nw)
		return false
	}
	c.WalkNetworks(l)

	return list
}

func isIngressNetwork(name string) bool {
	return name == "ingress"
}

var ingressChan = make(chan struct{}, 1)

func ingressWait() func() {
	ingressChan <- struct{}{}
	return func() { <-ingressChan }
}

// SetupIngress setups ingress networking.
func (daemon *Daemon) SetupIngress(create clustertypes.NetworkCreateRequest, nodeIP string) error {
	ip, _, err := net.ParseCIDR(nodeIP)
	if err != nil {
		return err
	}

	go func() {
		controller := daemon.netController
		controller.AgentInitWait()

		if n, err := daemon.GetNetworkByName(create.Name); err == nil && n != nil && n.ID() != create.ID {
			if err := controller.SandboxDestroy("ingress-sbox"); err != nil {
				logrus.Errorf("Failed to delete stale ingress sandbox: %v", err)
				return
			}

			// Cleanup any stale endpoints that might be left over during previous iterations
			epList := n.Endpoints()
			for _, ep := range epList {
				if err := ep.Delete(true); err != nil {
					logrus.Errorf("Failed to delete endpoint %s (%s): %v", ep.Name(), ep.ID(), err)
				}
			}

			if err := n.Delete(); err != nil {
				logrus.Errorf("Failed to delete stale ingress network %s: %v", n.ID(), err)
				return
			}
		}

		if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
			// If it is any other error other than already
			// exists error log error and return.
			if _, ok := err.(libnetwork.NetworkNameError); !ok {
				logrus.Errorf("Failed creating ingress network: %v", err)
				return
			}

			// Otherwise continue down the call to create or recreate sandbox.
		}

		n, err := daemon.GetNetworkByID(create.ID)
		if err != nil {
			logrus.Errorf("Failed getting ingress network by id after creating: %v", err)
			return
		}

		sb, err := controller.NewSandbox("ingress-sbox", libnetwork.OptionIngress())
		if err != nil {
			logrus.Errorf("Failed creating ingress sandbox: %v", err)
			return
		}

		ep, err := n.CreateEndpoint("ingress-endpoint", libnetwork.CreateOptionIpam(ip, nil, nil, nil))
		if err != nil {
			logrus.Errorf("Failed creating ingress endpoint: %v", err)
			return
		}

		if err := ep.Join(sb, nil); err != nil {
			logrus.Errorf("Failed joining ingress sandbox to ingress endpoint: %v", err)
		}
	}()

	return nil
}

// SetNetworkBootstrapKeys sets the bootstrap keys.
func (daemon *Daemon) SetNetworkBootstrapKeys(keys []*networktypes.EncryptionKey) error {
	return daemon.netController.SetKeys(keys)
}

// CreateManagedNetwork creates an agent network.
func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
	_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
	return err
}

// CreateNetwork creates a network with the given name, driver and other optional parameters
func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
	resp, err := daemon.createNetwork(create, "", false)
	if err != nil {
		return nil, err
	}
	return resp, err
}

func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
	// If there is a pending ingress network creation wait here
	// since ingress network creation can happen via node download
	// from manager or task download.
	if isIngressNetwork(create.Name) {
		defer ingressWait()()
	}

	if runconfig.IsPreDefinedNetwork(create.Name) && !agent {
		err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
		return nil, errors.NewRequestForbiddenError(err)
	}

	var warning string
	nw, err := daemon.GetNetworkByName(create.Name)
	if err != nil {
		if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
			return nil, err
		}
	}
	if nw != nil {
		if create.CheckDuplicate {
			return nil, libnetwork.NetworkNameError(create.Name)
		}
		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
	}

	c := daemon.netController
	driver := create.Driver
	if driver == "" {
		driver = c.Config().Daemon.DefaultDriver
	}

	ipam := create.IPAM
	v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
	if err != nil {
		return nil, err
	}

	nwOptions := []libnetwork.NetworkOption{
		libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options),
		libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
		libnetwork.NetworkOptionDriverOpts(create.Options),
		libnetwork.NetworkOptionLabels(create.Labels),
	}
	if create.Internal {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
	}
	if agent {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionDynamic())
		nwOptions = append(nwOptions, libnetwork.NetworkOptionPersist(false))
	}

	if isIngressNetwork(create.Name) {
		nwOptions = append(nwOptions, libnetwork.NetworkOptionIngress())
	}

	n, err := c.NewNetwork(driver, create.Name, id, nwOptions...)
	if err != nil {
		return nil, err
	}

	daemon.LogNetworkEvent(n, "create")
	return &types.NetworkCreateResponse{
		ID:      n.ID(),
		Warning: warning,
	}, nil
}

func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) {
	ipamV4Cfg := []*libnetwork.IpamConf{}
	ipamV6Cfg := []*libnetwork.IpamConf{}
	for _, d := range data {
		iCfg := libnetwork.IpamConf{}
		iCfg.PreferredPool = d.Subnet
		iCfg.SubPool = d.IPRange
		iCfg.Gateway = d.Gateway
		iCfg.AuxAddresses = d.AuxAddress
		ip, _, err := net.ParseCIDR(d.Subnet)
		if err != nil {
			return nil, nil, fmt.Errorf("Invalid subnet %s : %v", d.Subnet, err)
		}
		if ip.To4() != nil {
			ipamV4Cfg = append(ipamV4Cfg, &iCfg)
		} else {
			ipamV6Cfg = append(ipamV6Cfg, &iCfg)
		}
	}
	return ipamV4Cfg, ipamV6Cfg, nil
}

// UpdateContainerServiceConfig updates a service configuration.
func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		return err
	}

	container.NetworkSettings.Service = serviceConfig
	return nil
}

// ConnectContainerToNetwork connects the given container to the given
// network. If either cannot be found, an err is returned. If the
// network cannot be set up, an err is returned.
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		return err
	}
	return daemon.ConnectToNetwork(container, networkName, endpointConfig)
}

// DisconnectContainerFromNetwork disconnects the given container from
// the given network. If either cannot be found, an err is returned.
func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error {
	container, err := daemon.GetContainer(containerName)
	if err != nil {
		if force {
			return daemon.ForceEndpointDelete(containerName, network)
		}
		return err
	}
	return daemon.DisconnectFromNetwork(container, network, force)
}

// GetNetworkDriverList returns the list of plugins drivers
// registered for network.
func (daemon *Daemon) GetNetworkDriverList() map[string]bool {
	pluginList := make(map[string]bool)

	if !daemon.NetworkControllerEnabled() {
		return nil
	}
	c := daemon.netController
	networks := c.Networks()

	for _, network := range networks {
		driver := network.Type()
		pluginList[driver] = true
	}
	// TODO : Replace this with proper libnetwork API
	pluginList["overlay"] = true

	return pluginList
}

// DeleteManagedNetwork deletes an agent network.
func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
	return daemon.deleteNetwork(networkID, true)
}

// DeleteNetwork destroys a network unless it's one of docker's predefined networks.
func (daemon *Daemon) DeleteNetwork(networkID string) error {
	return daemon.deleteNetwork(networkID, false)
}

func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
	nw, err := daemon.FindNetwork(networkID)
	if err != nil {
		return err
	}

	if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
		err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
		return errors.NewRequestForbiddenError(err)
	}

	if err := nw.Delete(); err != nil {
		return err
	}
	daemon.LogNetworkEvent(nw, "destroy")
	return nil
}

// GetNetworks returns a list of all networks
func (daemon *Daemon) GetNetworks() []libnetwork.Network {
	return daemon.getAllNetworks()
}
<|endoftext|>


<commit_before>package daemon

import (
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/container"
	"github.com/docker/docker/daemon/execdriver"
	derr "github.com/docker/docker/errors"
	"github.com/docker/docker/volume"
	"github.com/docker/engine-api/types"
	containertypes "github.com/docker/engine-api/types/container"
	"github.com/opencontainers/runc/libcontainer/label"
)

var (
	// ErrVolumeReadonly is used to signal an error when trying to copy data into
	// a volume mount that is not writable.
	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
)

type mounts []execdriver.Mount

// volumeToAPIType converts a volume.Volume to the type used by the remote API
func volumeToAPIType(v volume.Volume) *types.Volume {
	return &types.Volume{
		Name:       v.Name(),
		Driver:     v.DriverName(),
		Mountpoint: v.Path(),
	}
}

// Len returns the number of mounts. Used in sorting.
func (m mounts) Len() int {
	return len(m)
}

// Less returns true if the number of parts (a/b/c would be 3 parts) in the
// mount indexed by parameter 1 is less than that of the mount indexed by
// parameter 2. Used in sorting.
func (m mounts) Less(i, j int) bool {
	return m.parts(i) < m.parts(j)
}

// Swap swaps two items in an array of mounts. Used in sorting
func (m mounts) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

// parts returns the number of parts in the destination of a mount. Used in sorting.
func (m mounts) parts(i int) int {
	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
}

// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
// It follows the next sequence to decide what to mount in each final destination:
//
// 1. Select the previously configured mount points for the containers, if any.
// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
// 4. Cleanup old volumes that are about to be reassigned.
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) error {
	binds := map[string]bool{}
	mountPoints := map[string]*volume.MountPoint{}

	// 1. Read already configured mount points.
	for name, point := range container.MountPoints {
		mountPoints[name] = point
	}

	// 2. Read volumes from other containers.
	for _, v := range hostConfig.VolumesFrom {
		containerID, mode, err := volume.ParseVolumesFrom(v)
		if err != nil {
			return err
		}

		c, err := daemon.GetContainer(containerID)
		if err != nil {
			return err
		}

		for _, m := range c.MountPoints {
			cp := &volume.MountPoint{
				Name:        m.Name,
				Source:      m.Source,
				RW:          m.RW && volume.ReadWrite(mode),
				Driver:      m.Driver,
				Destination: m.Destination,
				Propagation: m.Propagation,
				Named:       m.Named,
			}

			if len(cp.Source) == 0 {
				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
				if err != nil {
					return err
				}
				cp.Volume = v
			}

			mountPoints[cp.Destination] = cp
		}
	}

	// 3. Read bind mounts
	for _, b := range hostConfig.Binds {
		// #10618
		bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver)
		if err != nil {
			return err
		}

		if binds[bind.Destination] {
			return derr.ErrorCodeMountDup.WithArgs(bind.Destination)
		}

		if len(bind.Name) > 0 && len(bind.Driver) > 0 {
			// create the volume
			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil)
			if err != nil {
				return err
			}
			bind.Volume = v
			bind.Source = v.Path()
			// bind.Name is an already existing volume, we need to use that here
			bind.Driver = v.DriverName()
			bind.Named = true
			bind = setBindModeIfNull(bind)
		}
		if label.RelabelNeeded(bind.Mode) {
			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
				return err
			}
		}
		binds[bind.Destination] = true
		mountPoints[bind.Destination] = bind
	}

	container.Lock()

	// 4. Cleanup old volumes that are about to be reassigned.
	for _, m := range mountPoints {
		if m.BackwardsCompatible() {
			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
				daemon.volumes.Dereference(mp.Volume, container.ID)
			}
		}
	}
	container.MountPoints = mountPoints

	container.Unlock()

	return nil
}

// lazyInitializeVolume initializes a mountpoint's volume if needed.
// This happens after a daemon restart.
func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
	if len(m.Driver) > 0 && m.Volume == nil {
		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
		if err != nil {
			return err
		}
		m.Volume = v
	}
	return nil
}
<commit_msg>Do not relabel if user did not request it for non local volumes<commit_after>package daemon

import (
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/container"
	"github.com/docker/docker/daemon/execdriver"
	derr "github.com/docker/docker/errors"
	"github.com/docker/docker/volume"
	"github.com/docker/engine-api/types"
	containertypes "github.com/docker/engine-api/types/container"
	"github.com/opencontainers/runc/libcontainer/label"
)

var (
	// ErrVolumeReadonly is used to signal an error when trying to copy data into
	// a volume mount that is not writable.
	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
)

type mounts []execdriver.Mount

// volumeToAPIType converts a volume.Volume to the type used by the remote API
func volumeToAPIType(v volume.Volume) *types.Volume {
	return &types.Volume{
		Name:       v.Name(),
		Driver:     v.DriverName(),
		Mountpoint: v.Path(),
	}
}

// Len returns the number of mounts. Used in sorting.
func (m mounts) Len() int {
	return len(m)
}

// Less returns true if the number of parts (a/b/c would be 3 parts) in the
// mount indexed by parameter 1 is less than that of the mount indexed by
// parameter 2. Used in sorting.
func (m mounts) Less(i, j int) bool {
	return m.parts(i) < m.parts(j)
}

// Swap swaps two items in an array of mounts. Used in sorting
func (m mounts) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

// parts returns the number of parts in the destination of a mount. Used in sorting.
func (m mounts) parts(i int) int {
	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
}

// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
// It follows the next sequence to decide what to mount in each final destination:
//
// 1. Select the previously configured mount points for the containers, if any.
// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
// 4. Cleanup old volumes that are about to be reassigned.
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) error {
	binds := map[string]bool{}
	mountPoints := map[string]*volume.MountPoint{}

	// 1. Read already configured mount points.
	for name, point := range container.MountPoints {
		mountPoints[name] = point
	}

	// 2. Read volumes from other containers.
	for _, v := range hostConfig.VolumesFrom {
		containerID, mode, err := volume.ParseVolumesFrom(v)
		if err != nil {
			return err
		}

		c, err := daemon.GetContainer(containerID)
		if err != nil {
			return err
		}

		for _, m := range c.MountPoints {
			cp := &volume.MountPoint{
				Name:        m.Name,
				Source:      m.Source,
				RW:          m.RW && volume.ReadWrite(mode),
				Driver:      m.Driver,
				Destination: m.Destination,
				Propagation: m.Propagation,
				Named:       m.Named,
			}

			if len(cp.Source) == 0 {
				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
				if err != nil {
					return err
				}
				cp.Volume = v
			}

			mountPoints[cp.Destination] = cp
		}
	}

	// 3. Read bind mounts
	for _, b := range hostConfig.Binds {
		// #10618
		bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver)
		if err != nil {
			return err
		}

		if binds[bind.Destination] {
			return derr.ErrorCodeMountDup.WithArgs(bind.Destination)
		}

		if len(bind.Name) > 0 && len(bind.Driver) > 0 {
			// create the volume
			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil)
			if err != nil {
				return err
			}
			bind.Volume = v
			bind.Source = v.Path()
			// bind.Name is an already existing volume, we need to use that here
			bind.Driver = v.DriverName()
			bind.Named = true
			if bind.Driver == "local" {
				bind = setBindModeIfNull(bind)
			}
		}
		if label.RelabelNeeded(bind.Mode) {
			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
				return err
			}
		}
		binds[bind.Destination] = true
		mountPoints[bind.Destination] = bind
	}

	container.Lock()

	// 4. Cleanup old volumes that are about to be reassigned.
	for _, m := range mountPoints {
		if m.BackwardsCompatible() {
			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
				daemon.volumes.Dereference(mp.Volume, container.ID)
			}
		}
	}
	container.MountPoints = mountPoints

	container.Unlock()

	return nil
}

// lazyInitializeVolume initializes a mountpoint's volume if needed.
// This happens after a daemon restart.
func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
	if len(m.Driver) > 0 && m.Volume == nil {
		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
		if err != nil {
			return err
		}
		m.Volume = v
	}
	return nil
}
<|endoftext|>


<commit_before>package daemon

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/types"
	derr "github.com/docker/docker/errors"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/volume"
	"github.com/docker/docker/volume/drivers"
)

var (
	// ErrVolumeReadonly is used to signal an error when trying to copy data into
	// a volume mount that is not writable.
	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
	// ErrVolumeInUse is a typed error returned when trying to remove a volume that is currently in use by a container
	ErrVolumeInUse = errors.New("volume is in use")
	// ErrNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store
	ErrNoSuchVolume = errors.New("no such volume")
)

// mountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type mountPoint struct {
	Name        string
	Destination string
	Driver      string
	RW          bool
	Volume      volume.Volume `json:"-"`
	Source      string
	Mode        string `json:"Relabel"` // Originally field was `Relabel`"
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *mountPoint) Setup() (string, error) {
	if m.Volume != nil {
		return m.Volume.Mount()
	}

	if len(m.Source) > 0 {
		if _, err := os.Stat(m.Source); err != nil {
			if !os.IsNotExist(err) {
				return "", err
			}
			logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source)
			if err := system.MkdirAll(m.Source, 0755); err != nil {
				return "", err
			}
		}
		return m.Source, nil
	}

	return "", derr.ErrorCodeMountSetup
}

// hasResource checks whether the given absolute path for a container is in
// this mount point. If the relative path starts with `../` then the resource
// is outside of this mount point, but we can't simply check for this prefix
// because it misses `..` which is also outside of the mount, so check both.
func (m *mountPoint) hasResource(absolutePath string) bool {
	relPath, err := filepath.Rel(m.Destination, absolutePath)

	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
}

// Path returns the path of a volume in a mount point.
func (m *mountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}

	return m.Source
}

// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
	volList, err := ioutil.ReadDir(source)
	if err != nil {
		return err
	}
	if len(volList) > 0 {
		srcList, err := ioutil.ReadDir(destination)
		if err != nil {
			return err
		}
		if len(srcList) == 0 {
			// If the source volume is empty copy files from the root into the volume
			if err := chrootarchive.CopyWithTar(source, destination); err != nil {
				return err
			}
		}
	}
	return copyOwnership(source, destination)
}

func newVolumeStore(vols []volume.Volume) *volumeStore {
	store := &volumeStore{
		vols: make(map[string]*volumeCounter),
	}
	for _, v := range vols {
		store.vols[v.Name()] = &volumeCounter{v, 0}
	}
	return store
}

// volumeStore is a struct that stores the list of volumes available and keeps track of their usage counts
type volumeStore struct {
	vols map[string]*volumeCounter
	mu   sync.Mutex
}

type volumeCounter struct {
	volume.Volume
	count int
}

func getVolumeDriver(name string) (volume.Driver, error) {
	if name == "" {
		name = volume.DefaultDriverName
	}
	return volumedrivers.Lookup(name)
}

// Create tries to find an existing volume with the given name or create a new one from the passed in driver
func (s *volumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) {
	s.mu.Lock()
	if vc, exists := s.vols[name]; exists {
		v := vc.Volume
		s.mu.Unlock()
		return v, nil
	}
	s.mu.Unlock()
	logrus.Debugf("Registering new volume reference: driver %s, name %s", driverName, name)

	vd, err := getVolumeDriver(driverName)
	if err != nil {
		return nil, err
	}

	v, err := vd.Create(name, opts)
	if err != nil {
		return nil, err
	}

	s.mu.Lock()
	s.vols[v.Name()] = &volumeCounter{v, 0}
	s.mu.Unlock()

	return v, nil
}

// Get looks if a volume with the given name exists and returns it if so
func (s *volumeStore) Get(name string) (volume.Volume, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	vc, exists := s.vols[name]
	if !exists {
		return nil, ErrNoSuchVolume
	}
	return vc.Volume, nil
}

// Remove removes the requested volume. A volume is not removed if the usage count is > 0
func (s *volumeStore) Remove(v volume.Volume) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	name := v.Name()
	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
	vc, exists := s.vols[name]
	if !exists {
		return ErrNoSuchVolume
	}

	if vc.count != 0 {
		return ErrVolumeInUse
	}

	vd, err := getVolumeDriver(vc.DriverName())
	if err != nil {
		return err
	}
	if err := vd.Remove(vc.Volume); err != nil {
		return err
	}
	delete(s.vols, name)
	return nil
}

// Increment increments the usage count of the passed in volume by 1
func (s *volumeStore) Increment(v volume.Volume) {
	s.mu.Lock()
	defer s.mu.Unlock()
	logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())

	vc, exists := s.vols[v.Name()]
	if !exists {
		s.vols[v.Name()] = &volumeCounter{v, 1}
		return
	}
	vc.count++
	return
}

// Decrement decrements the usage count of the passed in volume by 1
func (s *volumeStore) Decrement(v volume.Volume) {
	s.mu.Lock()
	defer s.mu.Unlock()
	logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())

	vc, exists := s.vols[v.Name()]
	if !exists {
		return
	}
	vc.count--
	return
}

// Count returns the usage count of the passed in volume
func (s *volumeStore) Count(v volume.Volume) int {
	s.mu.Lock()
	defer s.mu.Unlock()
	vc, exists := s.vols[v.Name()]
	if !exists {
		return 0
	}
	return vc.count
}

// List returns all the available volumes
func (s *volumeStore) List() []volume.Volume {
	s.mu.Lock()
	defer s.mu.Unlock()
	var ls []volume.Volume
	for _, vc := range s.vols {
		ls = append(ls, vc.Volume)
	}
	return ls
}

// volumeToAPIType converts a volume.Volume to the type used by the remote API
func volumeToAPIType(v volume.Volume) *types.Volume {
	return &types.Volume{
		Name:       v.Name(),
		Driver:     v.DriverName(),
		Mountpoint: v.Path(),
	}
}
<commit_msg>Cleanup: remove unnecessary return at the end of block in volumes.go<commit_after>package daemon

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/types"
	derr "github.com/docker/docker/errors"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/volume"
	"github.com/docker/docker/volume/drivers"
)

var (
	// ErrVolumeReadonly is used to signal an error when trying to copy data into
	// a volume mount that is not writable.
	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
	// ErrVolumeInUse is a typed error returned when trying to remove a volume that is currently in use by a container
	ErrVolumeInUse = errors.New("volume is in use")
	// ErrNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store
	ErrNoSuchVolume = errors.New("no such volume")
)

// mountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type mountPoint struct {
	Name        string
	Destination string
	Driver      string
	RW          bool
	Volume      volume.Volume `json:"-"`
	Source      string
	Mode        string `json:"Relabel"` // Originally field was `Relabel`"
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *mountPoint) Setup() (string, error) {
	if m.Volume != nil {
		return m.Volume.Mount()
	}

	if len(m.Source) > 0 {
		if _, err := os.Stat(m.Source); err != nil {
			if !os.IsNotExist(err) {
				return "", err
			}
			logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source)
			if err := system.MkdirAll(m.Source, 0755); err != nil {
				return "", err
			}
		}
		return m.Source, nil
	}

	return "", derr.ErrorCodeMountSetup
}

// hasResource checks whether the given absolute path for a container is in
// this mount point. If the relative path starts with `../` then the resource
// is outside of this mount point, but we can't simply check for this prefix
// because it misses `..` which is also outside of the mount, so check both.
func (m *mountPoint) hasResource(absolutePath string) bool {
	relPath, err := filepath.Rel(m.Destination, absolutePath)

	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
}

// Path returns the path of a volume in a mount point.
func (m *mountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}

	return m.Source
}

// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
	volList, err := ioutil.ReadDir(source)
	if err != nil {
		return err
	}
	if len(volList) > 0 {
		srcList, err := ioutil.ReadDir(destination)
		if err != nil {
			return err
		}
		if len(srcList) == 0 {
			// If the source volume is empty copy files from the root into the volume
			if err := chrootarchive.CopyWithTar(source, destination); err != nil {
				return err
			}
		}
	}
	return copyOwnership(source, destination)
}

func newVolumeStore(vols []volume.Volume) *volumeStore {
	store := &volumeStore{
		vols: make(map[string]*volumeCounter),
	}
	for _, v := range vols {
		store.vols[v.Name()] = &volumeCounter{v, 0}
	}
	return store
}

// volumeStore is a struct that stores the list of volumes available and keeps track of their usage counts
type volumeStore struct {
	vols map[string]*volumeCounter
	mu   sync.Mutex
}

type volumeCounter struct {
	volume.Volume
	count int
}

func getVolumeDriver(name string) (volume.Driver, error) {
	if name == "" {
		name = volume.DefaultDriverName
	}
	return volumedrivers.Lookup(name)
}

// Create tries to find an existing volume with the given name or create a new one from the passed in driver
func (s *volumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) {
	s.mu.Lock()
	if vc, exists := s.vols[name]; exists {
		v := vc.Volume
		s.mu.Unlock()
		return v, nil
	}
	s.mu.Unlock()
	logrus.Debugf("Registering new volume reference: driver %s, name %s", driverName, name)

	vd, err := getVolumeDriver(driverName)
	if err != nil {
		return nil, err
	}

	v, err := vd.Create(name, opts)
	if err != nil {
		return nil, err
	}

	s.mu.Lock()
	s.vols[v.Name()] = &volumeCounter{v, 0}
	s.mu.Unlock()

	return v, nil
}

// Get looks if a volume with the given name exists and returns it if so
func (s *volumeStore) Get(name string) (volume.Volume, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	vc, exists := s.vols[name]
	if !exists {
		return nil, ErrNoSuchVolume
	}
	return vc.Volume, nil
}

// Remove removes the requested volume. A volume is not removed if the usage count is > 0
func (s *volumeStore) Remove(v volume.Volume) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	name := v.Name()
	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
	vc, exists := s.vols[name]
	if !exists {
		return ErrNoSuchVolume
	}

	if vc.count != 0 {
		return ErrVolumeInUse
	}

	vd, err := getVolumeDriver(vc.DriverName())
	if err != nil {
		return err
	}
	if err := vd.Remove(vc.Volume); err != nil {
		return err
	}
	delete(s.vols, name)
	return nil
}

// Increment increments the usage count of the passed in volume by 1
func (s *volumeStore) Increment(v volume.Volume) {
	s.mu.Lock()
	defer s.mu.Unlock()
	logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())

	vc, exists := s.vols[v.Name()]
	if !exists {
		s.vols[v.Name()] = &volumeCounter{v, 1}
		return
	}
	vc.count++
}

// Decrement decrements the usage count of the passed in volume by 1
func (s *volumeStore) Decrement(v volume.Volume) {
	s.mu.Lock()
	defer s.mu.Unlock()
	logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())

	vc, exists := s.vols[v.Name()]
	if !exists {
		return
	}
	vc.count--
}

// Count returns the usage count of the passed in volume
func (s *volumeStore) Count(v volume.Volume) int {
	s.mu.Lock()
	defer s.mu.Unlock()
	vc, exists := s.vols[v.Name()]
	if !exists {
		return 0
	}
	return vc.count
}

// List returns all the available volumes
func (s *volumeStore) List() []volume.Volume {
	s.mu.Lock()
	defer s.mu.Unlock()
	var ls []volume.Volume
	for _, vc := range s.vols {
		ls = append(ls, vc.Volume)
	}
	return ls
}

// volumeToAPIType converts a volume.Volume to the type used by the remote API
func volumeToAPIType(v volume.Volume) *types.Volume {
	return &types.Volume{
		Name:       v.Name(),
		Driver:     v.DriverName(),
		Mountpoint: v.Path(),
	}
}
<|endoftext|>


<commit_before>//go:generate api15gen ./api_data.json
package rsapi15

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// RightScale API 1.5 client
// Instances of this struct should be created through `New`.
type Api15 struct {
	AccountId int           // Account in which client is currently operating
	Auth      Authenticator // Authenticator, signs requests for auth
	Logger    *log.Logger   // Optional logger, if specified requests and responses get logged
	Endpoint  string        // API endpoint, e.g. "us-3.rightscale.com"
	Client    *http.Client  // Underlying http client
}

// New returns a API 1.5 client that uses User oauth authentication.
// logger and client are optional.
// If no HTTP client is specified then the default client is used.
func New(accountId int, refreshToken string, logger *log.Logger, client *http.Client) (*Api15, error) {
	if client == nil {
		client = http.DefaultClient
	}
	auth := OAuthAuthenticator{
		RefreshToken: refreshToken,
		RefreshAt:    time.Now().Add(-1 * time.Hour),
		Client:       client,
	}
	endpoint, err := auth.ResolveEndpoint(accountId)
	if err != nil {
		return nil, err
	}
	return &Api15{
		AccountId: accountId,
		Auth:      &auth,
		Logger:    logger,
		Endpoint:  endpoint,
		Client:    client,
	}, nil
}

// Generic API parameters type
type ApiParams map[string]interface{}

// Do is a generic client method and is meant for command line tools and other higher level clients.
// It accepts a resource or resource collection href (e.g. "/api/servers"), the name of an action
// (e.g. "create") and the request parameters (payload or query strings).
// The method makes the request and returns the raw HTTP response or an error.
// The LoadResponse method can be used to load the response body if needed.
func (a *Api15) Do(href, action string, params ApiParams) (*http.Response, error) {
	// Allow "servers" instead of "/api/servers"
	if !strings.HasPrefix(href, "/api/") {
		href = "/api/" + href
	}

	// First figure out action verb and uri
	var uri = href
	var method string
	switch action {
	case "show", "index":
		method = "GET"
	case "create":
		method = "POST"
	case "update":
		method = "PUT"
	case "destroy":
		method = "DELETE"
	default:
		if pair, ok := actionMap[action]; ok {
			uri = href + pair[0]
			method = pair[1]
		}
	}

	// Now call corresponding low-level request method
	switch method {
	case "GET":
		return a.GetRaw(uri, params)
	case "POST":
		return a.PostRaw(uri, params)
	case "PUT":
		return nil, a.Put(uri, params)
	case "DELETE":
		return nil, a.Delete(uri)
	default:
		return nil, fmt.Errorf("Unknown href '%s' or action '%s'", href, action)
	}
}

// Low-level GET request that loads response JSON into generic object
func (a *Api15) Get(uri string, params ApiParams) (interface{}, error) {
	resp, err := a.GetRaw(uri, params)
	if err != nil {
		return nil, err
	}
	return a.LoadResponse(resp)
}

// Low-level GET request
func (a *Api15) GetRaw(uri string, params ApiParams) (*http.Response, error) {
	return a.makeRequest("GET", uri, params)
}

// Low-level POST request that loads response JSON into generic object
// Any "Location" header present in the HTTP response is returned in a map under the "Location" key.
func (a *Api15) Post(uri string, body ApiParams) (interface{}, error) {
	resp, err := a.PostRaw(uri, body)
	if err != nil {
		return nil, err
	}
	return a.LoadResponse(resp)
}

// Low-level POST request
func (a *Api15) PostRaw(uri string, body ApiParams) (*http.Response, error) {
	return a.makeRequest("POST", uri, body)
}

// Low-level PUT request
func (a *Api15) Put(uri string, body ApiParams) error {
	_, err := a.makeRequest("PUT", uri, body)
	return err
}

// Low-level DELETE request
func (a *Api15) Delete(uri string) error {
	_, err := a.makeRequest("DELETE", uri, nil)
	return err
}

// Deserialize JSON response into generic object.
// If the response has a "Location" header then the returned object is a map with one key "Location"
// containing the value of the header.
func (a *Api15) LoadResponse(resp *http.Response) (interface{}, error) {
	defer resp.Body.Close()
	var respBody interface{}
	jsonResp, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read response (%s)", err.Error())
	}
	if len(jsonResp) > 0 {
		err = json.Unmarshal(jsonResp, &respBody)
		if err != nil {
			return nil, fmt.Errorf("Failed to load response (%s)", err.Error())
		}
	}
	// Special case for "Location" header, assume that if there is a location there is no body
	loc := resp.Header.Get("Location")
	if len(loc) > 0 {
		var bodyMap = make(map[string]interface{})
		bodyMap["Location"] = loc
		respBody = interface{}(bodyMap)
	}
	return respBody, err
}

// Helper function that signs, makes and logs HTTP request
func (a *Api15) makeRequest(verb, uri string, params ApiParams) (*http.Response, error) {
	var body = bytes.NewBuffer([]byte{})
	if (verb == "POST" || verb == "PUT") && params != nil {
		var jsonBytes, err = json.Marshal(params)
		if err != nil {
			return nil, fmt.Errorf("Failed to serialize response (%s)", err.Error())
		}
		body = bytes.NewBuffer([]byte(jsonBytes))
	}
	var u = url.URL{
		Scheme: "https",
		Host:   a.Endpoint,
		Path:   uri,
	}
	if verb == "GET" && params != nil {
		for n, p := range params {
			switch t := p.(type) {
			case string:
				u.Query().Set(n, t)
			case []string:
				for _, e := range t {
					u.Query().Add(n, e)
				}
			default:
				return nil, fmt.Errorf("Invalid param value <%+v>, value must be a string or an array of strings", p)
			}
		}
	}
	var sUrl = u.String()
	var req, err = http.NewRequest(verb, sUrl, body)
	if err != nil {
		return nil, err
	}
	if err = a.Auth.Sign(req, a.Endpoint); err != nil {
		return nil, err
	}
	var id string
	var startedAt time.Time
	if a.Logger != nil {
		startedAt = time.Now()
		b := make([]byte, 6)
		io.ReadFull(rand.Reader, b)
		id = base64.StdEncoding.EncodeToString(b)
		a.Logger.Printf("[%s] %s %s", id, req.Method, sUrl)
	}
	resp, err := a.Client.Do(req)
	if err != nil {
		return nil, err
	}
	if a.Logger != nil {
		d := time.Since(startedAt)
		a.Logger.Printf("[%s] %s in %s", id, resp.Status, d.String())
	}
	return resp, err
}
<commit_msg>Add ability to specify endpoint when creating client.<commit_after>//go:generate api15gen ./api_data.json
package rsapi15

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// RightScale API 1.5 client
// Instances of this struct should be created through `New`.
type Api15 struct {
	AccountId int           // Account in which client is currently operating
	Auth      Authenticator // Authenticator, signs requests for auth
	Logger    *log.Logger   // Optional logger, if specified requests and responses get logged
	Endpoint  string        // API endpoint, e.g. "us-3.rightscale.com"
	Client    *http.Client  // Underlying http client
}

// New returns a API 1.5 client that uses User oauth authentication.
// logger and client are optional.
// endpoint may be blank in which case client attempts to resolve it using auth.
// If no HTTP client is specified then the default client is used.
func New(accountId int, refreshToken string, endpoint string, logger *log.Logger,
	client *http.Client) (*Api15, error) {
	if client == nil {
		client = http.DefaultClient
	}
	var auth = OAuthAuthenticator{
		RefreshToken: refreshToken,
		RefreshAt:    time.Now().Add(-1 * time.Hour),
		Client:       client,
	}
	if endpoint == "" {
		if resolved, err := auth.ResolveEndpoint(accountId); err != nil {
			return nil, err
		} else {
			endpoint = resolved
		}
	}
	return &Api15{
		AccountId: accountId,
		Auth:      &auth,
		Logger:    logger,
		Endpoint:  endpoint,
		Client:    client,
	}, nil
}

// Generic API parameters type
type ApiParams map[string]interface{}

// Do is a generic client method and is meant for command line tools and other higher level clients.
// It accepts a resource or resource collection href (e.g. "/api/servers"), the name of an action
// (e.g. "create") and the request parameters (payload or query strings).
// The method makes the request and returns the raw HTTP response or an error.
// The LoadResponse method can be used to load the response body if needed.
func (a *Api15) Do(href, action string, params ApiParams) (*http.Response, error) {
	// Allow "servers" instead of "/api/servers"
	if !strings.HasPrefix(href, "/api/") {
		href = "/api/" + href
	}

	// First figure out action verb and uri
	var uri = href
	var method string
	switch action {
	case "show", "index":
		method = "GET"
	case "create":
		method = "POST"
	case "update":
		method = "PUT"
	case "destroy":
		method = "DELETE"
	default:
		if pair, ok := actionMap[action]; ok {
			uri = href + pair[0]
			method = pair[1]
		}
	}

	// Now call corresponding low-level request method
	switch method {
	case "GET":
		return a.GetRaw(uri, params)
	case "POST":
		return a.PostRaw(uri, params)
	case "PUT":
		return nil, a.Put(uri, params)
	case "DELETE":
		return nil, a.Delete(uri)
	default:
		return nil, fmt.Errorf("Unknown href '%s' or action '%s'", href, action)
	}
}

// Low-level GET request that loads response JSON into generic object
func (a *Api15) Get(uri string, params ApiParams) (interface{}, error) {
	resp, err := a.GetRaw(uri, params)
	if err != nil {
		return nil, err
	}
	return a.LoadResponse(resp)
}

// Low-level GET request
func (a *Api15) GetRaw(uri string, params ApiParams) (*http.Response, error) {
	return a.makeRequest("GET", uri, params)
}

// Low-level POST request that loads response JSON into generic object
// Any "Location" header present in the HTTP response is returned in a map under the "Location" key.
func (a *Api15) Post(uri string, body ApiParams) (interface{}, error) {
	resp, err := a.PostRaw(uri, body)
	if err != nil {
		return nil, err
	}
	return a.LoadResponse(resp)
}

// Low-level POST request
func (a *Api15) PostRaw(uri string, body ApiParams) (*http.Response, error) {
	return a.makeRequest("POST", uri, body)
}

// Low-level PUT request
func (a *Api15) Put(uri string, body ApiParams) error {
	_, err := a.makeRequest("PUT", uri, body)
	return err
}

// Low-level DELETE request
func (a *Api15) Delete(uri string) error {
	_, err := a.makeRequest("DELETE", uri, nil)
	return err
}

// Deserialize JSON response into generic object.
// If the response has a "Location" header then the returned object is a map with one key "Location"
// containing the value of the header.
func (a *Api15) LoadResponse(resp *http.Response) (interface{}, error) {
	defer resp.Body.Close()
	var respBody interface{}
	jsonResp, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read response (%s)", err.Error())
	}
	if len(jsonResp) > 0 {
		err = json.Unmarshal(jsonResp, &respBody)
		if err != nil {
			return nil, fmt.Errorf("Failed to load response (%s)", err.Error())
		}
	}
	// Special case for "Location" header, assume that if there is a location there is no body
	loc := resp.Header.Get("Location")
	if len(loc) > 0 {
		var bodyMap = make(map[string]interface{})
		bodyMap["Location"] = loc
		respBody = interface{}(bodyMap)
	}
	return respBody, err
}

// Helper function that signs, makes and logs HTTP request
func (a *Api15) makeRequest(verb, uri string, params ApiParams) (*http.Response, error) {
	var body = bytes.NewBuffer([]byte{})
	if (verb == "POST" || verb == "PUT") && params != nil {
		var jsonBytes, err = json.Marshal(params)
		if err != nil {
			return nil, fmt.Errorf("Failed to serialize response (%s)", err.Error())
		}
		body = bytes.NewBuffer([]byte(jsonBytes))
	}
	var u = url.URL{
		Scheme: "https",
		Host:   a.Endpoint,
		Path:   uri,
	}
	if verb == "GET" && params != nil {
		for n, p := range params {
			switch t := p.(type) {
			case string:
				u.Query().Set(n, t)
			case []string:
				for _, e := range t {
					u.Query().Add(n, e)
				}
			default:
				return nil, fmt.Errorf("Invalid param value <%+v>, value must be a string or an array of strings", p)
			}
		}
	}
	var sUrl = u.String()
	var req, err = http.NewRequest(verb, sUrl, body)
	if err != nil {
		return nil, err
	}
	if err = a.Auth.Sign(req, a.Endpoint); err != nil {
		return nil, err
	}
	if a.AccountId > 0 {
		req.Header.Set("X-Account", strconv.Itoa(a.AccountId))
	}
	var id string
	var startedAt time.Time
	if a.Logger != nil {
		startedAt = time.Now()
		b := make([]byte, 6)
		io.ReadFull(rand.Reader, b)
		id = base64.StdEncoding.EncodeToString(b)
		a.Logger.Printf("[%s] %s %s", id, req.Method, sUrl)
	}
	resp, err := a.Client.Do(req)
	if err != nil {
		return nil, err
	}
	if a.Logger != nil {
		d := time.Since(startedAt)
		a.Logger.Printf("[%s] %s in %s", id, resp.Status, d.String())
	}
	return resp, err
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv4 implements IP-level socket options for the Internet
// Protocol version 4.
//
// The package provides IP-level socket options that allow
// manipulation of IPv4 facilities.  The IPv4 and basic host
// requirements for IPv4 are defined in RFC 791, RFC 1112, RFC 1122,
// RFC 3678 and RFC 4607.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv4 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv4.Conn is used to set the type-of-service field on
// the IPv4 header for each packet.
//
//	ln, err := net.Listen("tcp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv4 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.IPv4(224, 0, 0, 250)
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv4 and Ethernet.
//
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv4.PacketConn is used to enable control
// message transmissons.
//
//	if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, cm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if cm.Dst.IsMulticast() {
//			if cm.Dst.Equal(group)
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTOS(DiffServCS0)
//		p.SetTTL(16)
//		if _, err := p.WriteTo(data, nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			if err := p.SetMulticastInterface(ifi); err != nil {
//				// error handling
//			}
//			p.SetMulticastTTL(2)
//			if _, err := p.WriteTo(data, nil, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn or RawConn may join multiple
// multicast groups.  For example, a UDP listener with port 1024 might
// join two different groups across over two different network
// interfaces by using:
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv4.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	p2 := ipv4.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn or RawConn on IGMPv3 supported
// platform is able to join source-specific multicast groups as
// described in RFC 3678.  The application may use
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup for the
// operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
//	ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on IGMPv3 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// IGMPv1 or IGMP2 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv4 // import "golang.org/x/net/ipv4"
<commit_msg>ipv4: fix a typo<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv4 implements IP-level socket options for the Internet
// Protocol version 4.
//
// The package provides IP-level socket options that allow
// manipulation of IPv4 facilities.  The IPv4 and basic host
// requirements for IPv4 are defined in RFC 791, RFC 1112, RFC 1122,
// RFC 3678 and RFC 4607.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv4 transport.  When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv4.Conn is used to set the type-of-service field on
// the IPv4 header for each packet.
//
//	ln, err := net.Listen("tcp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv4 transport.  A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.IPv4(224, 0, 0, 250)
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces.  Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv4 and Ethernet.
//
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel.  When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv4.PacketConn is used to enable control
// message transmissons.
//
//	if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, cm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if cm.Dst.IsMulticast() {
//			if cm.Dst.Equal(group)
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTOS(DiffServCS0)
//		p.SetTTL(16)
//		if _, err := p.WriteTo(data, nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			if err := p.SetMulticastInterface(ifi); err != nil {
//				// error handling
//			}
//			p.SetMulticastTTL(2)
//			if _, err := p.WriteTo(data, nil, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn or RawConn may join multiple
// multicast groups.  For example, a UDP listener with port 1024 might
// join two different groups across over two different network
// interfaces by using:
//
//	c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv4.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group.  The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv4.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	p2 := ipv4.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn or RawConn on IGMPv3 supported
// platform is able to join source-specific multicast groups as
// described in RFC 3678.  The application may use
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup for the
// operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
//	ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on IGMPv3 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// IGMPv1 or IGMPv2 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv4 // import "golang.org/x/net/ipv4"
<|endoftext|>


<commit_before>// irc descibes the basic client functionalities of irken
package irc

import (
	"bufio"
	"net"
	"time"
)

const DEFAULT_PORT = "6667"

type Conn struct {
	// sockection info
	user string

	// network utils
	io   *bufio.ReadWriter
	sock *net.TCPConn

	// server properties
	pingFreq time.Duration
}

// NewConn takes a remote network address and a default nick.
// It then tries to connect to the supplied address with the
// properties of the supplied session
func NewConn(addr string, nick string) (*Conn, error) {

	//TODO: check if addr already contains another port then the default port.

	addr += ":" + DEFAULT_PORT
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	sock, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return nil, err
	}

	io := bufio.NewReadWriter(bufio.NewReader(sock), bufio.NewWriter(sock))

	return &Conn{nick, io, sock, time.Minute}, nil
}

func (c *Conn) Read() {
	//TODO
}

func (c *Conn) Write() {
	//TODO
}

func (c *Conn) Close() {
	//TODO
}
<commit_msg>basic functionality in irc package<commit_after>// irc descibes the basic client functionalities of irken
package irc

import (
	"bufio"
	"net"
	"time"
)

const DEFAULT_PORT = "6667"

type Conn struct {
	// user info
	nick string

	// network utils
	io   *bufio.ReadWriter
	sock *net.TCPConn

	// server properties
	pingFreq time.Duration
}

// NewConn takes a remote network address and a default nick.
// It then tries to connect to the supplied address with the
// properties of the supplied session
func NewConn(addr string, nick string) (*Conn, error) {

	//TODO: check if addr already contains another port then the default port.

	addr += ":" + DEFAULT_PORT
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	sock, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return nil, err
	}

	io := bufio.NewReadWriter(bufio.NewReader(sock), bufio.NewWriter(sock))

	return &Conn{nick, io, sock, time.Minute}, nil
}

// Read attempts to read from the connected server and puts the data into io buffer.
// It will wait until it gets one line of a string.
func (c *Conn) Read() (string, error){
	data, err := c.io.ReadString('\n')
	return data, err
}

// Write attempts to write a string into the io buffer and
// flushes it, sending the data to the connected server
func (c *Conn) Write(s string) error{
	_, err := c.io.WriteString(s + "\r\n")
	if err != nil {
		return err
	}
	err = c.io.Flush()
	if err != nil {
		return err
	}

	return nil
}

// Close attempts to close the connection to the server
func (c *Conn) Close() {
	c.sock.Close()
}
<|endoftext|>