<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"org/apache/htrace/common"
	"org/apache/htrace/conf"
)

// A golang client for htraced.
// TODO: fancier APIs for streaming spans in the background, optimize TCP stuff
func NewClient(cnf *conf.Config, testHooks *TestHooks) (*Client, error) {
	hcl := Client{testHooks: testHooks}
	hcl.restAddr = cnf.Get(conf.HTRACE_WEB_ADDRESS)
	if testHooks != nil && testHooks.HrpcDisabled {
		hcl.hrpcAddr = ""
	} else {
		hcl.hrpcAddr = cnf.Get(conf.HTRACE_HRPC_ADDRESS)
	}
	return &hcl, nil
}

type TestHooks struct {
	// If true, HRPC is disabled.
	HrpcDisabled bool

	// A function which gets called after we connect to the server and send the
	// message frame, but before we write the message body.
	HandleWriteRequestBody func()
}

type Client struct {
	// REST address of the htraced server.
	restAddr string

	// HRPC address of the htraced server.
	hrpcAddr string

	// The test hooks to use, or nil if test hooks are not enabled.
	testHooks *TestHooks
}

// Get the htraced server version information.
func (hcl *Client) GetServerVersion() (*common.ServerVersion, error) {
	buf, _, err := hcl.makeGetRequest("server/info")
	if err != nil {
		return nil, err
	}
	var info common.ServerVersion
	err = json.Unmarshal(buf, &info)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &info, nil
}

// Get the htraced server debug information.
func (hcl *Client) GetServerDebugInfo() (*common.ServerDebugInfo, error) {
	buf, _, err := hcl.makeGetRequest("server/debugInfo")
	if err != nil {
		return nil, err
	}
	var debugInfo common.ServerDebugInfo
	err = json.Unmarshal(buf, &debugInfo)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &debugInfo, nil
}

// Get the htraced server statistics.
func (hcl *Client) GetServerStats() (*common.ServerStats, error) {
	buf, _, err := hcl.makeGetRequest("server/stats")
	if err != nil {
		return nil, err
	}
	var stats common.ServerStats
	err = json.Unmarshal(buf, &stats)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &stats, nil
}

// Get the htraced server statistics.
func (hcl *Client) GetServerConf() (map[string]string, error) {
	buf, _, err := hcl.makeGetRequest("server/conf")
	if err != nil {
		return nil, err
	}
	cnf := make(map[string]string)
	err = json.Unmarshal(buf, &cnf)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return cnf, nil
}

// Get information about a trace span.  Returns nil, nil if the span was not found.
func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) {
	buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%s", sid.String()))
	if err != nil {
		if rc == http.StatusNoContent {
			return nil, nil
		}
		return nil, err
	}
	var span common.Span
	err = json.Unmarshal(buf, &span)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &span, nil
}

func (hcl *Client) WriteSpans(req *common.WriteSpansReq) error {
	if hcl.hrpcAddr == "" {
		return hcl.writeSpansHttp(req)
	}
	hcr, err := newHClient(hcl.hrpcAddr, hcl.testHooks)
	if err != nil {
		return err
	}
	defer hcr.Close()
	return hcr.writeSpans(req)
}

func (hcl *Client) writeSpansHttp(req *common.WriteSpansReq) error {
	var w bytes.Buffer
	enc := json.NewEncoder(&w)
	err := enc.Encode(req)
	if err != nil {
		return errors.New(fmt.Sprintf("Error serializing span: %s",
			err.Error()))
	}
	_, _, err = hcl.makeRestRequest("POST", "writeSpans", &w)
	if err != nil {
		return err
	}
	return nil
}

// Find the child IDs of a given span ID.
func (hcl *Client) FindChildren(sid common.SpanId, lim int) ([]common.SpanId, error) {
	buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%s/children?lim=%d",
		sid.String(), lim))
	if err != nil {
		return nil, err
	}
	var spanIds []common.SpanId
	err = json.Unmarshal(buf, &spanIds)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return spanIds, nil
}

// Make a query
func (hcl *Client) Query(query *common.Query) ([]common.Span, error) {
	in, err := json.Marshal(query)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error marshalling query: %s", err.Error()))
	}
	var out []byte
	var url = fmt.Sprintf("query?query=%s", in)
	out, _, err = hcl.makeGetRequest(url)
	if err != nil {
		return nil, err
	}
	var spans []common.Span
	err = json.Unmarshal(out, &spans)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshalling results: %s", err.Error()))
	}
	return spans, nil
}

func (hcl *Client) makeGetRequest(reqName string) ([]byte, int, error) {
	return hcl.makeRestRequest("GET", reqName, nil)
}

// Make a general JSON REST request.
// Returns the request body, the response code, and the error.
// Note: if the response code is non-zero, the error will also be non-zero.
func (hcl *Client) makeRestRequest(reqType string, reqName string,
	reqBody io.Reader) ([]byte, int, error) {
	url := fmt.Sprintf("http://%s/%s",
		hcl.restAddr, reqName)
	req, err := http.NewRequest(reqType, url, reqBody)
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, -1, errors.New(fmt.Sprintf("Error: error making http request to %s: %s\n", url,
			err.Error()))
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, resp.StatusCode,
			errors.New(fmt.Sprintf("Error: got bad response status from %s: %s\n", url, resp.Status))
	}
	var body []byte
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, -1, errors.New(fmt.Sprintf("Error: error reading response body: %s\n", err.Error()))
	}
	return body, 0, nil
}

// Dump all spans from the htraced daemon.
func (hcl *Client) DumpAll(lim int, out chan *common.Span) error {
	defer func() {
		close(out)
	}()
	searchId := common.INVALID_SPAN_ID
	for {
		q := common.Query{
			Lim: lim,
			Predicates: []common.Predicate{
				common.Predicate{
					Op:    "ge",
					Field: "spanid",
					Val:   searchId.String(),
				},
			},
		}
		spans, err := hcl.Query(&q)
		if err != nil {
			return errors.New(fmt.Sprintf("Error querying spans with IDs at or after "+
				"%s: %s", searchId.String(), err.Error()))
		}
		if len(spans) == 0 {
			return nil
		}
		for i := range spans {
			out <- &spans[i]
		}
		searchId = spans[len(spans)-1].Id.Next()
	}
}

func (hcl *Client) Close() {
	hcl.restAddr = ""
	hcl.hrpcAddr = ""
}
<commit_msg>HTRACE-310. htracedTool: when there is an error response, print the body of the response (Colin Patrick McCabe via iwasakims)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"org/apache/htrace/common"
	"org/apache/htrace/conf"
)

// A golang client for htraced.
// TODO: fancier APIs for streaming spans in the background, optimize TCP stuff
func NewClient(cnf *conf.Config, testHooks *TestHooks) (*Client, error) {
	hcl := Client{testHooks: testHooks}
	hcl.restAddr = cnf.Get(conf.HTRACE_WEB_ADDRESS)
	if testHooks != nil && testHooks.HrpcDisabled {
		hcl.hrpcAddr = ""
	} else {
		hcl.hrpcAddr = cnf.Get(conf.HTRACE_HRPC_ADDRESS)
	}
	return &hcl, nil
}

type TestHooks struct {
	// If true, HRPC is disabled.
	HrpcDisabled bool

	// A function which gets called after we connect to the server and send the
	// message frame, but before we write the message body.
	HandleWriteRequestBody func()
}

type Client struct {
	// REST address of the htraced server.
	restAddr string

	// HRPC address of the htraced server.
	hrpcAddr string

	// The test hooks to use, or nil if test hooks are not enabled.
	testHooks *TestHooks
}

// Get the htraced server version information.
func (hcl *Client) GetServerVersion() (*common.ServerVersion, error) {
	buf, _, err := hcl.makeGetRequest("server/info")
	if err != nil {
		return nil, err
	}
	var info common.ServerVersion
	err = json.Unmarshal(buf, &info)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &info, nil
}

// Get the htraced server debug information.
func (hcl *Client) GetServerDebugInfo() (*common.ServerDebugInfo, error) {
	buf, _, err := hcl.makeGetRequest("server/debugInfo")
	if err != nil {
		return nil, err
	}
	var debugInfo common.ServerDebugInfo
	err = json.Unmarshal(buf, &debugInfo)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &debugInfo, nil
}

// Get the htraced server statistics.
func (hcl *Client) GetServerStats() (*common.ServerStats, error) {
	buf, _, err := hcl.makeGetRequest("server/stats")
	if err != nil {
		return nil, err
	}
	var stats common.ServerStats
	err = json.Unmarshal(buf, &stats)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &stats, nil
}

// Get the htraced server statistics.
func (hcl *Client) GetServerConf() (map[string]string, error) {
	buf, _, err := hcl.makeGetRequest("server/conf")
	if err != nil {
		return nil, err
	}
	cnf := make(map[string]string)
	err = json.Unmarshal(buf, &cnf)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return cnf, nil
}

// Get information about a trace span.  Returns nil, nil if the span was not found.
func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) {
	buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%s", sid.String()))
	if err != nil {
		if rc == http.StatusNoContent {
			return nil, nil
		}
		return nil, err
	}
	var span common.Span
	err = json.Unmarshal(buf, &span)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return &span, nil
}

func (hcl *Client) WriteSpans(req *common.WriteSpansReq) error {
	if hcl.hrpcAddr == "" {
		return hcl.writeSpansHttp(req)
	}
	hcr, err := newHClient(hcl.hrpcAddr, hcl.testHooks)
	if err != nil {
		return err
	}
	defer hcr.Close()
	return hcr.writeSpans(req)
}

func (hcl *Client) writeSpansHttp(req *common.WriteSpansReq) error {
	var w bytes.Buffer
	enc := json.NewEncoder(&w)
	err := enc.Encode(req)
	if err != nil {
		return errors.New(fmt.Sprintf("Error serializing span: %s",
			err.Error()))
	}
	_, _, err = hcl.makeRestRequest("POST", "writeSpans", &w)
	if err != nil {
		return err
	}
	return nil
}

// Find the child IDs of a given span ID.
func (hcl *Client) FindChildren(sid common.SpanId, lim int) ([]common.SpanId, error) {
	buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%s/children?lim=%d",
		sid.String(), lim))
	if err != nil {
		return nil, err
	}
	var spanIds []common.SpanId
	err = json.Unmarshal(buf, &spanIds)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
			"body %s: %s", string(buf), err.Error()))
	}
	return spanIds, nil
}

// Make a query
func (hcl *Client) Query(query *common.Query) ([]common.Span, error) {
	in, err := json.Marshal(query)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error marshalling query: %s", err.Error()))
	}
	var out []byte
	var url = fmt.Sprintf("query?query=%s", in)
	out, _, err = hcl.makeGetRequest(url)
	if err != nil {
		return nil, err
	}
	var spans []common.Span
	err = json.Unmarshal(out, &spans)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshalling results: %s", err.Error()))
	}
	return spans, nil
}

func (hcl *Client) makeGetRequest(reqName string) ([]byte, int, error) {
	return hcl.makeRestRequest("GET", reqName, nil)
}

// Make a general JSON REST request.
// Returns the request body, the response code, and the error.
// Note: if the response code is non-zero, the error will also be non-zero.
func (hcl *Client) makeRestRequest(reqType string, reqName string,
	reqBody io.Reader) ([]byte, int, error) {
	url := fmt.Sprintf("http://%s/%s",
		hcl.restAddr, reqName)
	req, err := http.NewRequest(reqType, url, reqBody)
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, -1, errors.New(fmt.Sprintf("Error: error making http request to %s: %s\n", url,
			err.Error()))
	}
	defer resp.Body.Close()
	body, err2 := ioutil.ReadAll(resp.Body)
	if err2 != nil {
		return nil, -1, errors.New(fmt.Sprintf("Error: error reading response body: %s\n", err2.Error()))
	}
	if resp.StatusCode != http.StatusOK {
		return nil, resp.StatusCode,
			errors.New(fmt.Sprintf("Error: got bad response status from " +
				"%s: %s\n%s\n", url, resp.Status, body))
	}
	return body, 0, nil
}

// Dump all spans from the htraced daemon.
func (hcl *Client) DumpAll(lim int, out chan *common.Span) error {
	defer func() {
		close(out)
	}()
	searchId := common.INVALID_SPAN_ID
	for {
		q := common.Query{
			Lim: lim,
			Predicates: []common.Predicate{
				common.Predicate{
					Op:    "ge",
					Field: "spanid",
					Val:   searchId.String(),
				},
			},
		}
		spans, err := hcl.Query(&q)
		if err != nil {
			return errors.New(fmt.Sprintf("Error querying spans with IDs at or after "+
				"%s: %s", searchId.String(), err.Error()))
		}
		if len(spans) == 0 {
			return nil
		}
		for i := range spans {
			out <- &spans[i]
		}
		searchId = spans[len(spans)-1].Id.Next()
	}
}

func (hcl *Client) Close() {
	hcl.restAddr = ""
	hcl.hrpcAddr = ""
}
<|endoftext|>


<commit_before>package db

import (
	"net/http"
	"strings"

	"github.com/couchbase/sg-bucket"
	"github.com/couchbase/sync_gateway/auth"
	"github.com/couchbase/sync_gateway/base"
	ch "github.com/couchbase/sync_gateway/channels"
)

type DesignDoc sgbucket.DesignDoc

const (
	DesignDocSyncGateway      = "sync_gateway"
	DesignDocSyncHousekeeping = "sync_housekeeping"
	ViewPrincipals            = "principals"
	ViewChannels              = "channels"
	ViewAccess                = "access"
	ViewAccessVbSeq           = "access_vbseq"
	ViewRoleAccess            = "role_access"
	ViewAllBits               = "all_bits"
	ViewAllDocs               = "all_docs"
	ViewImport                = "import"
	ViewOldRevs               = "old_revs"
	ViewSessions              = "sessions"
)

func isInternalDDoc(ddocName string) bool {
	return strings.HasPrefix(ddocName, "sync_")
}

// Enforces access by admins only, and not to the built-in Sync Gateway design docs:
func (db *Database) checkDDocAccess(ddocName string) error {
	if db.user != nil || isInternalDDoc(ddocName) {
		return base.HTTPErrorf(http.StatusForbidden, "forbidden")
	}
	return nil
}

func (db *Database) GetDesignDoc(ddocName string, result interface{}) (err error) {
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.GetDDoc(ddocName, result)
	}
	return
}

func (db *Database) PutDesignDoc(ddocName string, ddoc DesignDoc) (err error) {
	wrap := true
	if opts := ddoc.Options; opts != nil {
		if opts.Raw == true {
			wrap = false
		}
	}
	if wrap {
		wrapViews(&ddoc, db.GetUserViewsEnabled())
	}
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.PutDDoc(ddocName, ddoc)
	}
	return
}

func wrapViews(ddoc *DesignDoc, enableUserViews bool) {
	// Wrap the map functions to ignore special docs and strip _sync metadata.  If user views are enabled, also
	// add channel filtering.
	if enableUserViews {
		for name, view := range ddoc.Views {
			view.Map = `function(doc,meta) {
		                    var sync = doc._sync;
		                    if (sync === undefined || meta.id.substring(0,6) == "_sync:")
		                      return;
		                    if ((sync.flags & 1) || sync.deleted)
		                      return;
		                    var channels = [];
		                    var channelMap = sync.channels;
							if (channelMap) {
								for (var name in channelMap) {
									removed = channelMap[name];
									if (!removed)
										channels.push(name);
								}
							}
		                    delete doc._sync;
		                    meta.rev = sync.rev;
		                    meta.channels = channels;

		                    var _emit = emit;
		                    (function(){
			                    var emit = function(key,value) {
			                    	_emit(key,[channels, value]);
			                    };
								(` + view.Map + `) (doc, meta);
							}());
							doc._sync = sync;
						}`
			ddoc.Views[name] = view // view is not a pointer, so have to copy it back
		}
	} else {
		for name, view := range ddoc.Views {
			view.Map = `function(doc,meta) {
	                    var sync = doc._sync;
	                    if (sync === undefined || meta.id.substring(0,6) == "_sync:")
	                      return;
	                    if ((sync.flags & 1) || sync.deleted)
	                      return;
	                    delete doc._sync;
	                    meta.rev = sync.rev;
						(` + view.Map + `) (doc, meta); }`
			ddoc.Views[name] = view // view is not a pointer, so have to copy it back
		}
	}
}

func (db *Database) DeleteDesignDoc(ddocName string) (err error) {
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.DeleteDDoc(ddocName)
	}
	return
}

func (db *Database) QueryDesignDoc(ddocName string, viewName string, options map[string]interface{}) (*sgbucket.ViewResult, error) {

	// Regular users have limitations on what they can query
	if db.user != nil {
		// * Regular users can only query when user views are enabled
		if !db.GetUserViewsEnabled() {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
		// * Admins can query any design doc including the internal ones
		// * Regular users can query non-internal design docs
		if isInternalDDoc(ddocName) {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
	}

	result, err := db.Bucket.View(ddocName, viewName, options)
	if err != nil {
		return nil, err
	}
	if isInternalDDoc(ddocName) {
		if options["include_docs"] == true {
			for _, row := range result.Rows {
				stripSyncProperty(row)
			}
		}
	} else {
		applyChannelFiltering := options["reduce"] != true && db.GetUserViewsEnabled()
		result = filterViewResult(result, db.user, applyChannelFiltering)
	}
	return &result, nil
}

// Cleans up the Value property, and removes rows that aren't visible to the current user
func filterViewResult(input sgbucket.ViewResult, user auth.User, applyChannelFiltering bool) (result sgbucket.ViewResult) {
	hasStarChannel := false
	var visibleChannels ch.TimedSet
	if user != nil {
		visibleChannels = user.InheritedChannels()
		hasStarChannel = !visibleChannels.Contains("*")
		if !applyChannelFiltering {
			return // this is an error
		}
	}
	result.TotalRows = input.TotalRows
	result.Rows = make([]*sgbucket.ViewRow, 0, len(input.Rows)/2)
	for _, row := range input.Rows {
		if applyChannelFiltering {
			value, ok := row.Value.([]interface{})
			if ok {
				// value[0] is the array of channels; value[1] is the actual value
				if !hasStarChannel || channelsIntersect(visibleChannels, value[0].([]interface{})) {
					// Add this row:
					stripSyncProperty(row)
					result.Rows = append(result.Rows, &sgbucket.ViewRow{
						Key:   row.Key,
						Value: value[1],
						ID:    row.ID,
						Doc:   row.Doc,
					})
				}
			}
		} else {
			// Add the raw row:
			stripSyncProperty(row)
			result.Rows = append(result.Rows, &sgbucket.ViewRow{
				Key:   row.Key,
				Value: row.Value,
				ID:    row.ID,
				Doc:   row.Doc,
			})
		}

	}
	return
}

// Is any item of channels found in visibleChannels?
func channelsIntersect(visibleChannels ch.TimedSet, channels []interface{}) bool {
	for _, channel := range channels {
		if visibleChannels.Contains(channel.(string)) || channel == "*" {
			return true
		}
	}
	return false
}

func stripSyncProperty(row *sgbucket.ViewRow) {
	if doc := row.Doc; doc != nil {
		delete((*doc).(map[string]interface{}), "_sync")
	}
}
<commit_msg>Fix for query issue when using multiple views within a design doc<commit_after>package db

import (
	"net/http"
	"strings"

	"github.com/couchbase/sg-bucket"
	"github.com/couchbase/sync_gateway/auth"
	"github.com/couchbase/sync_gateway/base"
	ch "github.com/couchbase/sync_gateway/channels"
)

type DesignDoc sgbucket.DesignDoc

const (
	DesignDocSyncGateway      = "sync_gateway"
	DesignDocSyncHousekeeping = "sync_housekeeping"
	ViewPrincipals            = "principals"
	ViewChannels              = "channels"
	ViewAccess                = "access"
	ViewAccessVbSeq           = "access_vbseq"
	ViewRoleAccess            = "role_access"
	ViewAllBits               = "all_bits"
	ViewAllDocs               = "all_docs"
	ViewImport                = "import"
	ViewOldRevs               = "old_revs"
	ViewSessions              = "sessions"
)

func isInternalDDoc(ddocName string) bool {
	return strings.HasPrefix(ddocName, "sync_")
}

// Enforces access by admins only, and not to the built-in Sync Gateway design docs:
func (db *Database) checkDDocAccess(ddocName string) error {
	if db.user != nil || isInternalDDoc(ddocName) {
		return base.HTTPErrorf(http.StatusForbidden, "forbidden")
	}
	return nil
}

func (db *Database) GetDesignDoc(ddocName string, result interface{}) (err error) {
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.GetDDoc(ddocName, result)
	}
	return
}

func (db *Database) PutDesignDoc(ddocName string, ddoc DesignDoc) (err error) {
	wrap := true
	if opts := ddoc.Options; opts != nil {
		if opts.Raw == true {
			wrap = false
		}
	}
	if wrap {
		wrapViews(&ddoc, db.GetUserViewsEnabled())
	}
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.PutDDoc(ddocName, ddoc)
	}
	return
}

func wrapViews(ddoc *DesignDoc, enableUserViews bool) {
	// Wrap the map functions to ignore special docs and strip _sync metadata.  If user views are enabled, also
	// add channel filtering.
	if enableUserViews {
		for name, view := range ddoc.Views {
			view.Map = `function(doc,meta) {
		                    var sync = doc._sync;
		                    if (sync === undefined || meta.id.substring(0,6) == "_sync:")
		                      return;
		                    if ((sync.flags & 1) || sync.deleted)
		                      return;
		                    var channels = [];
		                    var channelMap = sync.channels;
							if (channelMap) {
								for (var name in channelMap) {
									removed = channelMap[name];
									if (!removed)
										channels.push(name);
								}
							}
		                    delete doc._sync;
		                    meta.rev = sync.rev;
		                    meta.channels = channels;

		                    var _emit = emit;
		                    (function(){
			                    var emit = function(key,value) {
			                    	_emit(key,[channels, value]);
			                    };
								(` + view.Map + `) (doc, meta);
							}());
							doc._sync = sync;
						}`
			ddoc.Views[name] = view // view is not a pointer, so have to copy it back
		}
	} else {
		for name, view := range ddoc.Views {
			view.Map = `function(doc,meta) {
	                    var sync = doc._sync;
	                    if (sync === undefined || meta.id.substring(0,6) == "_sync:")
	                      return;
	                    if ((sync.flags & 1) || sync.deleted)
	                      return;
	                    delete doc._sync;
	                    meta.rev = sync.rev;
						(` + view.Map + `) (doc, meta);
						doc._sync = sync;}`
			ddoc.Views[name] = view // view is not a pointer, so have to copy it back
		}
	}
}

func (db *Database) DeleteDesignDoc(ddocName string) (err error) {
	if err = db.checkDDocAccess(ddocName); err == nil {
		err = db.Bucket.DeleteDDoc(ddocName)
	}
	return
}

func (db *Database) QueryDesignDoc(ddocName string, viewName string, options map[string]interface{}) (*sgbucket.ViewResult, error) {

	// Regular users have limitations on what they can query
	if db.user != nil {
		// * Regular users can only query when user views are enabled
		if !db.GetUserViewsEnabled() {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
		// * Admins can query any design doc including the internal ones
		// * Regular users can query non-internal design docs
		if isInternalDDoc(ddocName) {
			return nil, base.HTTPErrorf(http.StatusForbidden, "forbidden")
		}
	}

	result, err := db.Bucket.View(ddocName, viewName, options)
	if err != nil {
		return nil, err
	}
	if isInternalDDoc(ddocName) {
		if options["include_docs"] == true {
			for _, row := range result.Rows {
				stripSyncProperty(row)
			}
		}
	} else {
		applyChannelFiltering := options["reduce"] != true && db.GetUserViewsEnabled()
		result = filterViewResult(result, db.user, applyChannelFiltering)
	}
	return &result, nil
}

// Cleans up the Value property, and removes rows that aren't visible to the current user
func filterViewResult(input sgbucket.ViewResult, user auth.User, applyChannelFiltering bool) (result sgbucket.ViewResult) {
	hasStarChannel := false
	var visibleChannels ch.TimedSet
	if user != nil {
		visibleChannels = user.InheritedChannels()
		hasStarChannel = !visibleChannels.Contains("*")
		if !applyChannelFiltering {
			return // this is an error
		}
	}
	result.TotalRows = input.TotalRows
	result.Rows = make([]*sgbucket.ViewRow, 0, len(input.Rows)/2)
	for _, row := range input.Rows {
		if applyChannelFiltering {
			value, ok := row.Value.([]interface{})
			if ok {
				// value[0] is the array of channels; value[1] is the actual value
				if !hasStarChannel || channelsIntersect(visibleChannels, value[0].([]interface{})) {
					// Add this row:
					stripSyncProperty(row)
					result.Rows = append(result.Rows, &sgbucket.ViewRow{
						Key:   row.Key,
						Value: value[1],
						ID:    row.ID,
						Doc:   row.Doc,
					})
				}
			}
		} else {
			// Add the raw row:
			stripSyncProperty(row)
			result.Rows = append(result.Rows, &sgbucket.ViewRow{
				Key:   row.Key,
				Value: row.Value,
				ID:    row.ID,
				Doc:   row.Doc,
			})
		}

	}
	return
}

// Is any item of channels found in visibleChannels?
func channelsIntersect(visibleChannels ch.TimedSet, channels []interface{}) bool {
	for _, channel := range channels {
		if visibleChannels.Contains(channel.(string)) || channel == "*" {
			return true
		}
	}
	return false
}

func stripSyncProperty(row *sgbucket.ViewRow) {
	if doc := row.Doc; doc != nil {
		delete((*doc).(map[string]interface{}), "_sync")
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	stdtesting "testing"
	"time"

	. "launchpad.net/gocheck"
	"launchpad.net/gomaasapi"
	"launchpad.net/juju-core/environs"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils"
)

func TestMAAS(t *stdtesting.T) {
	TestingT(t)
}

type ProviderSuite struct {
	testing.LoggingSuite
	environ              *maasEnviron
	testMAASObject       *gomaasapi.TestMAASObject
	restoreTimeouts func()
}

var _ = Suite(&ProviderSuite{})

func (s *ProviderSuite) SetUpSuite(c *C) {
	s.restoreTimeouts = envtesting.PatchAttemptStrategies(&shortAttempt)
	s.LoggingSuite.SetUpSuite(c)
	TestMAASObject := gomaasapi.NewTestMAAS("1.0")
	s.testMAASObject = TestMAASObject
	s.environ = &maasEnviron{name: "test env", maasClientUnlocked: &TestMAASObject.MAASObject}
}

func (s *ProviderSuite) SetUpTest(c *C) {
	s.LoggingSuite.SetUpTest(c)
}

func (s *ProviderSuite) TearDownTest(c *C) {
	s.testMAASObject.TestServer.Clear()
	s.LoggingSuite.TearDownTest(c)
}

func (s *ProviderSuite) TearDownSuite(c *C) {
	s.testMAASObject.Close()
	s.restoreTimeouts()
	s.LoggingSuite.TearDownSuite(c)
}
<commit_msg>Remove unused imports.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	stdtesting "testing"

	. "launchpad.net/gocheck"
	"launchpad.net/gomaasapi"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/testing"
)

func TestMAAS(t *stdtesting.T) {
	TestingT(t)
}

type ProviderSuite struct {
	testing.LoggingSuite
	environ         *maasEnviron
	testMAASObject  *gomaasapi.TestMAASObject
	restoreTimeouts func()
}

var _ = Suite(&ProviderSuite{})

func (s *ProviderSuite) SetUpSuite(c *C) {
	s.restoreTimeouts = envtesting.PatchAttemptStrategies(&shortAttempt)
	s.LoggingSuite.SetUpSuite(c)
	TestMAASObject := gomaasapi.NewTestMAAS("1.0")
	s.testMAASObject = TestMAASObject
	s.environ = &maasEnviron{name: "test env", maasClientUnlocked: &TestMAASObject.MAASObject}
}

func (s *ProviderSuite) SetUpTest(c *C) {
	s.LoggingSuite.SetUpTest(c)
}

func (s *ProviderSuite) TearDownTest(c *C) {
	s.testMAASObject.TestServer.Clear()
	s.LoggingSuite.TearDownTest(c)
}

func (s *ProviderSuite) TearDownSuite(c *C) {
	s.testMAASObject.Close()
	s.restoreTimeouts()
	s.LoggingSuite.TearDownSuite(c)
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package common

import (
	"errors"
	"fmt"
	"org/apache/htrace/conf"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
)

// A logSink is a place logs can be written to.
type logSink struct {
	path     logPath
	file     *os.File
	lock     sync.Mutex
	refCount int // protected by logFilesLock
}

// Write to the logSink.
func (sink *logSink) write(str string) {
	sink.lock.Lock()
	defer sink.lock.Unlock()
	_, err := sink.file.Write([]byte(str))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error logging to '%s': %s\n", sink.path, err.Error())
	}
}

// Unreference the logSink.  If there are no more references, and the logSink is
// closeable, then we will close it here.
func (sink *logSink) Unref() {
	logFilesLock.Lock()
	defer logFilesLock.Unlock()
	sink.refCount--
	if sink.refCount <= 0 {
		if sink.path.IsCloseable() {
			err := sink.file.Close()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error closing log file %s: %s\n",
					sink.path, err.Error())
			}
		}
		logSinks[sink.path] = nil
	}
}

type logPath string

// An empty LogPath represents "stdout."
const STDOUT_LOG_PATH = ""

// Convert a path to a logPath.
func logPathFromString(path string) logPath {
	if path == STDOUT_LOG_PATH {
		return logPath("")
	}
	absPath, err := filepath.Abs(path)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get absolute path of %s: %s\n",
			path, err.Error())
		return logPath(path)
	}
	return logPath(absPath)
}

// Convert the path to a human-readable string.
func (path logPath) String() string {
	if path == "" {
		return "(stdout)"
	} else {
		return string(path)
	}
}

// Return true if the path is closeable.  stdout is not closeable.
func (path logPath) IsCloseable() bool {
	return path != STDOUT_LOG_PATH
}

func (path logPath) Open() *logSink {
	if path == STDOUT_LOG_PATH {
		return &logSink{path: path, file: os.Stdout}
	}
	file, err := os.OpenFile(string(path), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		sink := &logSink{path: STDOUT_LOG_PATH, file: os.Stdout}
		fmt.Fprintf(os.Stderr, "Failed to open log file %s: %s\n",
			path, err.Error())
		return sink
	}
	return &logSink{path: path, file: file}
}

var logFilesLock sync.Mutex

var logSinks map[logPath]*logSink = make(map[logPath]*logSink)

func getOrCreateLogSink(pathStr string) *logSink {
	path := logPathFromString(pathStr)
	logFilesLock.Lock()
	defer logFilesLock.Unlock()
	sink := logSinks[path]
	if sink == nil {
		sink = path.Open()
		logSinks[path] = sink
	}
	sink.refCount++
	return sink
}

type Level int

const (
	TRACE Level = iota
	DEBUG
	INFO
	WARN
	ERROR
)

var levelToString map[Level]string = map[Level]string{
	TRACE: "TRACE",
	DEBUG: "DEBUG",
	INFO:  "INFO",
	WARN:  "WARN",
	ERROR: "ERROR",
}

func (level Level) String() string {
	return levelToString[level]
}

func (level Level) LogString() string {
	return level.String()[0:1]
}

func LevelFromString(str string) (Level, error) {
	for k, v := range levelToString {
		if strings.ToLower(v) == strings.ToLower(str) {
			return k, nil
		}
	}
	var levelNames sort.StringSlice
	levelNames = make([]string, len(levelToString))
	var i int
	for _, v := range levelToString {
		levelNames[i] = v
		i++
	}
	sort.Sort(levelNames)
	return TRACE, errors.New(fmt.Sprintf("No such level as '%s'.  Valid "+
		"levels are '%v'\n", str, levelNames))
}

type Logger struct {
	sink  *logSink
	Level Level
}

func NewLogger(faculty string, cnf *conf.Config) *Logger {
	path, level := parseConf(faculty, cnf)
	sink := getOrCreateLogSink(path)
	return &Logger{sink: sink, Level: level}
}

func parseConf(faculty string, cnf *conf.Config) (string, Level) {
	facultyLogPathKey := faculty + "." + conf.HTRACE_LOG_PATH
	var facultyLogPath string
	if cnf.Contains(facultyLogPathKey) {
		facultyLogPath = cnf.Get(facultyLogPathKey)
	} else {
		facultyLogPath = cnf.Get(conf.HTRACE_LOG_PATH)
	}
	facultyLogLevelKey := faculty + conf.HTRACE_LOG_LEVEL
	var facultyLogLevelStr string
	if cnf.Contains(facultyLogLevelKey) {
		facultyLogLevelStr = cnf.Get(facultyLogLevelKey)
	} else {
		facultyLogLevelStr = cnf.Get(conf.HTRACE_LOG_LEVEL)
	}
	level, err := LevelFromString(facultyLogLevelStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error configuring log level: %s.  Using TRACE.\n")
		level = TRACE
	}
	return facultyLogPath, level
}

func (lg *Logger) Trace(str string) {
	lg.Write(TRACE, str)
}

func (lg *Logger) Tracef(format string, v ...interface{}) {
	lg.Write(TRACE, fmt.Sprintf(format, v...))
}

func (lg *Logger) Debug(str string) {
	lg.Write(DEBUG, str)
}

func (lg *Logger) Debugf(format string, v ...interface{}) {
	lg.Write(DEBUG, fmt.Sprintf(format, v...))
}

func (lg *Logger) Info(str string) {
	lg.Write(INFO, str)
}

func (lg *Logger) Infof(format string, v ...interface{}) {
	lg.Write(INFO, fmt.Sprintf(format, v...))
}

func (lg *Logger) Warn(str string) error {
	lg.Write(WARN, str)
	return errors.New(str)
}

func (lg *Logger) Warnf(format string, v ...interface{}) error {
	str := fmt.Sprintf(format, v...)
	lg.Write(WARN, str)
	return errors.New(str)
}

func (lg *Logger) Error(str string) error {
	lg.Write(ERROR, str)
	return errors.New(str)
}

func (lg *Logger) Errorf(format string, v ...interface{}) error {
	str := fmt.Sprintf(format, v...)
	lg.Write(ERROR, str)
	return errors.New(str)
}

func (lg *Logger) Write(level Level, str string) {
	if level >= lg.Level {
		lg.sink.write(time.Now().Format(time.RFC3339) + " " +
			level.LogString() + ": " + str)
	}
}

//
// A few functions which can be used to determine if a certain level of tracing
// is enabled.  These are useful in situations when evaluating the parameters
// of a logging function is expensive.  (Note, however, that we don't pay the
// cost of string concatenation and manipulation when a log message doesn't
// trigger.)
//

func (lg *Logger) TraceEnabled() bool {
	return lg.Level >= TRACE
}

func (lg *Logger) DebugEnabled() bool {
	return lg.Level >= DEBUG
}

func (lg *Logger) InfoEnabled() bool {
	return lg.Level >= INFO
}

func (lg *Logger) WarnEnabled() bool {
	return lg.Level >= WARN
}

func (lg *Logger) ErrorEnabled() bool {
	return lg.Level >= ERROR
}

func (lg *Logger) Close() {
	lg.sink.Unref()
	lg.sink = nil
}
<commit_msg>HTRACE-289. Fix TraceEnabled, etc. logger methods for conditional logging (Masatake Iwasaki via Colin P. McCabe)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package common

import (
	"errors"
	"fmt"
	"org/apache/htrace/conf"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
)

// A logSink is a place logs can be written to.
type logSink struct {
	path     logPath
	file     *os.File
	lock     sync.Mutex
	refCount int // protected by logFilesLock
}

// Write to the logSink.
func (sink *logSink) write(str string) {
	sink.lock.Lock()
	defer sink.lock.Unlock()
	_, err := sink.file.Write([]byte(str))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error logging to '%s': %s\n", sink.path, err.Error())
	}
}

// Unreference the logSink.  If there are no more references, and the logSink is
// closeable, then we will close it here.
func (sink *logSink) Unref() {
	logFilesLock.Lock()
	defer logFilesLock.Unlock()
	sink.refCount--
	if sink.refCount <= 0 {
		if sink.path.IsCloseable() {
			err := sink.file.Close()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error closing log file %s: %s\n",
					sink.path, err.Error())
			}
		}
		logSinks[sink.path] = nil
	}
}

type logPath string

// An empty LogPath represents "stdout."
const STDOUT_LOG_PATH = ""

// Convert a path to a logPath.
func logPathFromString(path string) logPath {
	if path == STDOUT_LOG_PATH {
		return logPath("")
	}
	absPath, err := filepath.Abs(path)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get absolute path of %s: %s\n",
			path, err.Error())
		return logPath(path)
	}
	return logPath(absPath)
}

// Convert the path to a human-readable string.
func (path logPath) String() string {
	if path == "" {
		return "(stdout)"
	} else {
		return string(path)
	}
}

// Return true if the path is closeable.  stdout is not closeable.
func (path logPath) IsCloseable() bool {
	return path != STDOUT_LOG_PATH
}

func (path logPath) Open() *logSink {
	if path == STDOUT_LOG_PATH {
		return &logSink{path: path, file: os.Stdout}
	}
	file, err := os.OpenFile(string(path), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		sink := &logSink{path: STDOUT_LOG_PATH, file: os.Stdout}
		fmt.Fprintf(os.Stderr, "Failed to open log file %s: %s\n",
			path, err.Error())
		return sink
	}
	return &logSink{path: path, file: file}
}

var logFilesLock sync.Mutex

var logSinks map[logPath]*logSink = make(map[logPath]*logSink)

func getOrCreateLogSink(pathStr string) *logSink {
	path := logPathFromString(pathStr)
	logFilesLock.Lock()
	defer logFilesLock.Unlock()
	sink := logSinks[path]
	if sink == nil {
		sink = path.Open()
		logSinks[path] = sink
	}
	sink.refCount++
	return sink
}

type Level int

const (
	TRACE Level = iota
	DEBUG
	INFO
	WARN
	ERROR
)

var levelToString map[Level]string = map[Level]string{
	TRACE: "TRACE",
	DEBUG: "DEBUG",
	INFO:  "INFO",
	WARN:  "WARN",
	ERROR: "ERROR",
}

func (level Level) String() string {
	return levelToString[level]
}

func (level Level) LogString() string {
	return level.String()[0:1]
}

func LevelFromString(str string) (Level, error) {
	for k, v := range levelToString {
		if strings.ToLower(v) == strings.ToLower(str) {
			return k, nil
		}
	}
	var levelNames sort.StringSlice
	levelNames = make([]string, len(levelToString))
	var i int
	for _, v := range levelToString {
		levelNames[i] = v
		i++
	}
	sort.Sort(levelNames)
	return TRACE, errors.New(fmt.Sprintf("No such level as '%s'.  Valid "+
		"levels are '%v'\n", str, levelNames))
}

type Logger struct {
	sink  *logSink
	Level Level
}

func NewLogger(faculty string, cnf *conf.Config) *Logger {
	path, level := parseConf(faculty, cnf)
	sink := getOrCreateLogSink(path)
	return &Logger{sink: sink, Level: level}
}

func parseConf(faculty string, cnf *conf.Config) (string, Level) {
	facultyLogPathKey := faculty + "." + conf.HTRACE_LOG_PATH
	var facultyLogPath string
	if cnf.Contains(facultyLogPathKey) {
		facultyLogPath = cnf.Get(facultyLogPathKey)
	} else {
		facultyLogPath = cnf.Get(conf.HTRACE_LOG_PATH)
	}
	facultyLogLevelKey := faculty + conf.HTRACE_LOG_LEVEL
	var facultyLogLevelStr string
	if cnf.Contains(facultyLogLevelKey) {
		facultyLogLevelStr = cnf.Get(facultyLogLevelKey)
	} else {
		facultyLogLevelStr = cnf.Get(conf.HTRACE_LOG_LEVEL)
	}
	level, err := LevelFromString(facultyLogLevelStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error configuring log level: %s.  Using TRACE.\n")
		level = TRACE
	}
	return facultyLogPath, level
}

func (lg *Logger) Trace(str string) {
	lg.Write(TRACE, str)
}

func (lg *Logger) Tracef(format string, v ...interface{}) {
	lg.Write(TRACE, fmt.Sprintf(format, v...))
}

func (lg *Logger) Debug(str string) {
	lg.Write(DEBUG, str)
}

func (lg *Logger) Debugf(format string, v ...interface{}) {
	lg.Write(DEBUG, fmt.Sprintf(format, v...))
}

func (lg *Logger) Info(str string) {
	lg.Write(INFO, str)
}

func (lg *Logger) Infof(format string, v ...interface{}) {
	lg.Write(INFO, fmt.Sprintf(format, v...))
}

func (lg *Logger) Warn(str string) error {
	lg.Write(WARN, str)
	return errors.New(str)
}

func (lg *Logger) Warnf(format string, v ...interface{}) error {
	str := fmt.Sprintf(format, v...)
	lg.Write(WARN, str)
	return errors.New(str)
}

func (lg *Logger) Error(str string) error {
	lg.Write(ERROR, str)
	return errors.New(str)
}

func (lg *Logger) Errorf(format string, v ...interface{}) error {
	str := fmt.Sprintf(format, v...)
	lg.Write(ERROR, str)
	return errors.New(str)
}

func (lg *Logger) Write(level Level, str string) {
	if level >= lg.Level {
		lg.sink.write(time.Now().Format(time.RFC3339) + " " +
			level.LogString() + ": " + str)
	}
}

//
// A few functions which can be used to determine if a certain level of tracing
// is enabled.  These are useful in situations when evaluating the parameters
// of a logging function is expensive.  (Note, however, that we don't pay the
// cost of string concatenation and manipulation when a log message doesn't
// trigger.)
//

func (lg *Logger) TraceEnabled() bool {
	return lg.Level <= TRACE
}

func (lg *Logger) DebugEnabled() bool {
	return lg.Level <= DEBUG
}

func (lg *Logger) InfoEnabled() bool {
	return lg.Level <= INFO
}

func (lg *Logger) WarnEnabled() bool {
	return lg.Level <= WARN
}

func (lg *Logger) ErrorEnabled() bool {
	return lg.Level <= ERROR
}

func (lg *Logger) Close() {
	lg.sink.Unref()
	lg.sink = nil
}
<|endoftext|>


<commit_before>package elasticthought

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/couchbaselabs/logg"
	"github.com/tleyden/cbfs/client"
)

func EnvironmentSanityCheck(config Configuration) error {

	if err := CbfsSanityCheck(config); err != nil {
		return err
	}
	logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check passed")

	return nil

}

func CbfsReadWriteFile(config Configuration, destPath, content string) error {

	// get cbfs client
	// Create a cbfs client
	cbfs, err := cbfsclient.New(config.CbfsUrl)
	if err != nil {
		return err
	}

	// write to random cbfs file
	options := cbfsclient.PutOptions{
		ContentType: "text/plain",
	}

	buffer := bytes.NewBuffer([]byte(content))

	if err := cbfs.Put("", destPath, buffer, options); err != nil {
		return fmt.Errorf("Error writing %v to cbfs: %v", destPath, err)
	}

	// read contents from cbfs file
	reader, err := cbfs.Get(destPath)
	if err != nil {
		return err
	}
	defer reader.Close()

	bytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}

	if string(bytes) != content {
		return fmt.Errorf("Content did not match expected")
	}

	// delete contents on cbfs
	if err := cbfs.Rm(destPath); err != nil {
		return err
	}
	return nil

}

func CbfsSanityCheck(config Configuration) error {

	uuid := NewUuid() // use uuid so other nodes on cluster don't conflict
	numAttempts := 20
	for i := 0; i < numAttempts; i++ {
		filename := fmt.Sprintf("env_check_%v_%v", uuid, i)
		content := fmt.Sprintf("Hello %v_%v", uuid, i)
		err := CbfsReadWriteFile(config, filename, content)
		if err == nil {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity ok: %v", filename)
			return nil
		}
		logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity failed # %v: %v", i, filename)
		if i >= (numAttempts - 1) {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check giving up")
			return err
		} else {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check sleeping ..")
			time.Sleep(time.Duration(i) * time.Second)
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check done sleeping")
		}
	}
	return fmt.Errorf("Exhausted attempts")

}
<commit_msg>make cbfs sanity check stricter<commit_after>package elasticthought

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"github.com/couchbaselabs/logg"
	"github.com/tleyden/cbfs/client"
)

func EnvironmentSanityCheck(config Configuration) error {

	if err := CbfsSanityCheck(config); err != nil {
		return err
	}
	logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check passed")

	return nil

}

func CbfsReadWriteFile(config Configuration, destPath, content string) error {

	// get cbfs client
	// Create a cbfs client
	cbfs, err := cbfsclient.New(config.CbfsUrl)
	if err != nil {
		return err
	}

	// write to random cbfs file
	options := cbfsclient.PutOptions{
		ContentType: "text/plain",
	}

	buffer := bytes.NewBuffer([]byte(content))

	if err := cbfs.Put("", destPath, buffer, options); err != nil {
		return fmt.Errorf("Error writing %v to cbfs: %v", destPath, err)
	}

	// delete it after we are done
	defer func() {
		if err := cbfs.Rm(destPath); err != nil {
			logg.LogError(fmt.Errorf("Error deleting %v from cbfs", destPath))
		}
	}()

	// read contents from cbfs file
	reader, err := cbfs.Get(destPath)
	if err != nil {
		return err
	}
	defer reader.Close()

	bytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}

	if string(bytes) != content {
		return fmt.Errorf("Content did not match expected")
	}

	// now make sure the file shows up on at least 3 cbfs nodes
	for {
		// read contents from cbfs file
		log.Printf("Getting filehandle of %v", destPath)
		fileHandle, err := cbfs.OpenFile(destPath)
		if err != nil {
			logg.LogError(fmt.Errorf("Error calling OpenFile on %v: %v", destPath, err))
			return err
		}

		nodes := fileHandle.Nodes()
		if len(nodes) >= 3 {
			log.Printf("%v present on %v nodes, which is sufficient", destPath, len(nodes))
			return nil
		}

		log.Printf("%v only present on %v nodes, which is insufficient", destPath, len(nodes))

		time.Sleep(1 * time.Second)

	}

}

func CbfsSanityCheck(config Configuration) error {

	uuid := NewUuid() // use uuid so other nodes on cluster don't conflict
	numAttempts := 20
	for i := 0; i < numAttempts; i++ {
		filename := fmt.Sprintf("env_check_%v_%v", uuid, i)
		content := fmt.Sprintf("Hello %v_%v", uuid, i)
		err := CbfsReadWriteFile(config, filename, content)
		if err == nil {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity ok: %v", filename)
			return nil
		}
		logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity failed # %v: %v", i, filename)
		if i >= (numAttempts - 1) {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check giving up")
			return err
		} else {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check sleeping ..")
			time.Sleep(time.Duration(i) * time.Second)
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check done sleeping")
		}
	}
	return fmt.Errorf("Exhausted attempts")

}
<|endoftext|>


<commit_before>package base58

import (
	"encoding/hex"
	"testing"
)

var TestTable = [][]string{
	{"61", "2g", "C2dGTwc"},
	{"626262", "a3gV", "4jF5uERJAK"},
	{"636363", "aPEr", "4mT4krqUYJ"},
	{"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2", "BXF1HuEUCqeVzZdrKeJjG74rjeXxqJ7dW"},
	{"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", "13REmUhe2ckUKy1FvM7AMCdtyYq831yxM3QeyEu4"},
	{"516b6fcd0f", "ABnLTmg", "237LSrY9NUUas"},
	{"bf4f89001e670274dd", "3SEo3LWLoPntC", "GwDDDeduj1jpykc27e"},
	{"572e4794", "3EFU7m", "FamExfqCeza"},
	{"ecac89cad93923c02321", "EJDM8drfXA6uyA", "2W1Yd5Zu6WGyKVtHGMrH"},
	{"10c8511e", "Rt5zm", "3op3iuGMmhs"},
	{"00000000000000000000", "1111111111", "111111111146Momb"},
	{"", "", "3QJmnh"},
}

func TestEncode(t *testing.T) {

	for _, line := range TestTable {
		test := []byte(line[0])

		b10 := make([]byte, hex.DecodedLen(len(test)))
		hex.Decode(b10, test)
		// This method accepts base10
		hash, _ := Encode(b10)

		if string(hash) != line[1] {
			t.Error(line[0])
			t.Errorf("Encode mismatch expected: %s was: %s",
				line[1], hash)
		}

		if b := Check(b10); b != line[2] {
			t.Errorf("Encode check failed expected: %s was: %s",
				line[2], b)
		}
	}
}

func TestDecode(t *testing.T) {
	for _, line := range TestTable {
		hash, _, err := Decode([]byte(line[1]))
		if err != nil {
			t.Error(err)
		}
		b10 := make([]byte, hex.EncodedLen(len(hash)))
		hex.Encode(b10, hash)
		if string(b10) != line[0] {
			t.Error(line[0])
			t.Errorf("Decode mismatch expected: %s was: %s",
				line[1], b10)
		}

	}
}
<commit_msg>Playing with example documentation<commit_after>package base58

import (
	"encoding/hex"
	"fmt"
	"testing"
)

var TestTable = [][]string{
	{"61", "2g", "C2dGTwc"},
	{"626262", "a3gV", "4jF5uERJAK"},
	{"636363", "aPEr", "4mT4krqUYJ"},
	{"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2", "BXF1HuEUCqeVzZdrKeJjG74rjeXxqJ7dW"},
	{"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", "13REmUhe2ckUKy1FvM7AMCdtyYq831yxM3QeyEu4"},
	{"516b6fcd0f", "ABnLTmg", "237LSrY9NUUas"},
	{"bf4f89001e670274dd", "3SEo3LWLoPntC", "GwDDDeduj1jpykc27e"},
	{"572e4794", "3EFU7m", "FamExfqCeza"},
	{"ecac89cad93923c02321", "EJDM8drfXA6uyA", "2W1Yd5Zu6WGyKVtHGMrH"},
	{"10c8511e", "Rt5zm", "3op3iuGMmhs"},
	{"00000000000000000000", "1111111111", "111111111146Momb"},
	{"", "", "3QJmnh"},
}

func TestEncode(t *testing.T) {

	for _, line := range TestTable {
		test := []byte(line[0])

		b10 := make([]byte, hex.DecodedLen(len(test)))
		hex.Decode(b10, test)
		// This method accepts base10
		hash, _ := Encode(b10)

		if string(hash) != line[1] {
			t.Error(line[0])
			t.Errorf("Encode mismatch expected: %s was: %s",
				line[1], hash)
		}

		if b := Check(b10); b != line[2] {
			t.Errorf("Encode check failed expected: %s was: %s",
				line[2], b)
		}
	}
}

func ExampleEncode() {
	// Decode hex prior to passing it to Encode
	h := []byte("bf4f89001e670274dd")
	ten := make([]byte, hex.DecodedLen(len(h)))
	hex.Decode(ten, h)
	fmt.Printf("base10: %s\n", ten)
	fmt.Printf("hex: %x\n", ten)

	fifty, len := Encode(ten)
	fmt.Printf("len: %d text: %s\n", len, fifty)
	// Output:
	// base10: �O�gt�
	// hex: bf4f89001e670274dd
	// len: 13 text: 3SEo3LWLoPntC
}

func ExampleDecode() {
	fmt.Println("Hello")
}

func TestDecode(t *testing.T) {
	for _, line := range TestTable {
		hash, _, err := Decode([]byte(line[1]))
		if err != nil {
			t.Error(err)
		}
		b10 := make([]byte, hex.EncodedLen(len(hash)))
		hex.Encode(b10, hash)
		if string(b10) != line[0] {
			t.Error(line[0])
			t.Errorf("Decode mismatch expected: %s was: %s",
				line[1], b10)
		}

	}
}
<|endoftext|>


<commit_before>package gorm

import (
	"database/sql"
	"fmt"
	"reflect"
)

var hstoreType = reflect.TypeOf(Hstore{})

type Hstore map[string]*string
type postgres struct {
}

func (s *postgres) BinVar(i int) string {
	return fmt.Sprintf("$%v", i)
}

func (s *postgres) SupportLastInsertId() bool {
	return false
}

func (d *postgres) SqlTag(value reflect.Value, size int) string {
	switch value.Kind() {
	case reflect.Bool:
		return "boolean"
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		return "integer"
	case reflect.Int64, reflect.Uint64:
		return "bigint"
	case reflect.Float32, reflect.Float64:
		return "numeric"
	case reflect.String:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		}
		return "text"
	case reflect.Struct:
		if value.Type() == timeType {
			return "timestamp with time zone"
		}
	case reflect.Map:
		if value.Type() == reflect.TypeOf(map[string]sql.NullString{}) {
			return "hstore"
		}
	default:
		if _, ok := value.Interface().([]byte); ok {
			return "bytea"
		}
	}
	panic(fmt.Sprintf("invalid sql type %s (%s) for postgres", value.Type().Name(), value.Kind().String()))
}

func (s *postgres) PrimaryKeyTag(value reflect.Value, size int) string {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		return "serial PRIMARY KEY"
	case reflect.Int64, reflect.Uint64:
		return "bigserial PRIMARY KEY"
	default:
		panic("Invalid primary key type")
	}
}

func (s *postgres) ReturningStr(key string) string {
	return fmt.Sprintf("RETURNING \"%v\"", key)
}

func (s *postgres) Quote(key string) string {
	return fmt.Sprintf("\"%s\"", key)
}

func (s *postgres) HasTable(scope *Scope, tableName string) bool {
	var count int
	newScope := scope.New(nil)
	newScope.Raw(fmt.Sprintf("SELECT count(*) FROM INFORMATION_SCHEMA.tables where table_name = %v", newScope.AddToVars(tableName)))
	newScope.DB().QueryRow(newScope.Sql, newScope.SqlVars...).Scan(&count)
	return count > 0
}

func (s *postgres) HasColumn(scope *Scope, tableName string, columnName string) bool {
	var count int
	newScope := scope.New(nil)
	newScope.Raw(fmt.Sprintf("SELECT count(*) FROM information_schema.columns WHERE table_name = %v AND column_name = %v",
		newScope.AddToVars(tableName),
		newScope.AddToVars(columnName),
	))
	newScope.DB().QueryRow(newScope.Sql, newScope.SqlVars...).Scan(&count)
	return count > 0
}
<commit_msg>Social: Implement sql.Valuer interface for gorm.Hstore<commit_after>package gorm

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"reflect"

	"github.com/lib/pq/hstore"
)

var hstoreType = reflect.TypeOf(Hstore{})

type Hstore map[string]*string

func (h Hstore) Value() (driver.Value, error) {
	hstore := hstore.Hstore{Map: map[string]sql.NullString{}}
	if len(h) == 0 {
		return nil, nil
	}

	for key, value := range h {
		hstore.Map[key] = sql.NullString{*value, true}
	}
	return hstore.Value()
}

type postgres struct {
}

func (s *postgres) BinVar(i int) string {
	return fmt.Sprintf("$%v", i)
}

func (s *postgres) SupportLastInsertId() bool {
	return false
}

func (d *postgres) SqlTag(value reflect.Value, size int) string {
	switch value.Kind() {
	case reflect.Bool:
		return "boolean"
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		return "integer"
	case reflect.Int64, reflect.Uint64:
		return "bigint"
	case reflect.Float32, reflect.Float64:
		return "numeric"
	case reflect.String:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		}
		return "text"
	case reflect.Struct:
		if value.Type() == timeType {
			return "timestamp with time zone"
		}
	case reflect.Map:
		if value.Type() == reflect.TypeOf(map[string]sql.NullString{}) {
			return "hstore"
		}
	default:
		if _, ok := value.Interface().([]byte); ok {
			return "bytea"
		}
	}
	panic(fmt.Sprintf("invalid sql type %s (%s) for postgres", value.Type().Name(), value.Kind().String()))
}

func (s *postgres) PrimaryKeyTag(value reflect.Value, size int) string {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
		return "serial PRIMARY KEY"
	case reflect.Int64, reflect.Uint64:
		return "bigserial PRIMARY KEY"
	default:
		panic("Invalid primary key type")
	}
}

func (s *postgres) ReturningStr(key string) string {
	return fmt.Sprintf("RETURNING \"%v\"", key)
}

func (s *postgres) Quote(key string) string {
	return fmt.Sprintf("\"%s\"", key)
}

func (s *postgres) HasTable(scope *Scope, tableName string) bool {
	var count int
	newScope := scope.New(nil)
	newScope.Raw(fmt.Sprintf("SELECT count(*) FROM INFORMATION_SCHEMA.tables where table_name = %v", newScope.AddToVars(tableName)))
	newScope.DB().QueryRow(newScope.Sql, newScope.SqlVars...).Scan(&count)
	return count > 0
}

func (s *postgres) HasColumn(scope *Scope, tableName string, columnName string) bool {
	var count int
	newScope := scope.New(nil)
	newScope.Raw(fmt.Sprintf("SELECT count(*) FROM information_schema.columns WHERE table_name = %v AND column_name = %v",
		newScope.AddToVars(tableName),
		newScope.AddToVars(columnName),
	))
	newScope.DB().QueryRow(newScope.Sql, newScope.SqlVars...).Scan(&count)
	return count > 0
}
<|endoftext|>


<commit_before>package database

import (
	"errors"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/gomodule/redigo/redis"
)

var (
	ErrInvalidLockName = errors.New("invalid lock name")
	ErrAlreadyLocked   = errors.New("lock already acquired")
)

type Lock struct {
	sync.RWMutex
	redis *Redis
	name  string
	value string
	held  bool
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func (r *Redis) AcquireLock(name string) (*Lock, error) {
	if len(name) == 0 {
		return nil, ErrInvalidLockName
	}

	l := &Lock{
		redis: r,
		name:  "LOCK_" + name,
		value: strconv.Itoa(rand.Int()),
		held:  true,
	}

	conn := r.UnblockedGet()
	defer conn.Close()
	_, err := conn.Do("SET", l.name, l.value, "NX", "PX", "5000")
	if err == redis.ErrNil {
		return nil, ErrAlreadyLocked
	} else if err != nil {
		return nil, err
	}

	// Start the lock keepalive
	go l.keepalive()

	return l, nil
}

func (l *Lock) keepalive() {
	for {
		l.Lock()
		if l.held == false {
			l.Unlock()
			return
		}
		l.Unlock()
		valid, err := l.isValid()
		if err != nil {
			continue
		}
		if !valid {
			l.Lock()
			l.held = false
			l.Unlock()
			return
		}
		conn := l.redis.UnblockedGet()
		ok, err := redis.Bool(conn.Do("PEXPIRE", l.name, "5000"))
		conn.Close()
		if err != nil {
			continue
		}
		if !ok {
			l.held = false
			return
		}
		time.Sleep(1 * time.Second)
	}
}

func (l *Lock) isValid() (bool, error) {
	conn := l.redis.UnblockedGet()
	defer conn.Close()
	value, err := redis.String(conn.Do("GET", l.name))
	if err != nil && err != redis.ErrNil {
		return false, err
	}
	if value != l.value {
		return false, nil
	}
	return true, nil
}

func (l *Lock) Release() {
	l.Lock()
	if l.held == false {
		l.Unlock()
		return
	}
	l.held = false
	l.Unlock()
	conn := l.redis.UnblockedGet()
	defer conn.Close()
	v, _ := redis.String(conn.Do("GET", l.name))
	if v == l.value {
		// Delete the key only if we are still the owner
		conn.Do("DEL", l.name)
	}
}

func (l *Lock) Held() bool {
	l.RLock()
	defer l.RUnlock()
	return l.held
}
<commit_msg>dbupgrade: fix locking<commit_after>package database

import (
	"errors"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/gomodule/redigo/redis"
)

var (
	ErrInvalidLockName = errors.New("invalid lock name")
	ErrAlreadyLocked   = errors.New("lock already acquired")
)

type Lock struct {
	sync.RWMutex
	redis *Redis
	name  string
	value string
	held  bool
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func (r *Redis) AcquireLock(name string) (*Lock, error) {
	if len(name) == 0 {
		return nil, ErrInvalidLockName
	}

	l := &Lock{
		redis: r,
		name:  "LOCK_" + name,
		value: strconv.Itoa(rand.Int()),
		held:  true,
	}

	conn := r.UnblockedGet()
	defer conn.Close()
	_, err := redis.String(conn.Do("SET", l.name, l.value, "NX", "PX", "5000"))
	if err == redis.ErrNil {
		return nil, ErrAlreadyLocked
	} else if err != nil {
		return nil, err
	}

	// Start the lock keepalive
	go l.keepalive()

	return l, nil
}

func (l *Lock) keepalive() {
	for {
		l.Lock()
		if l.held == false {
			l.Unlock()
			return
		}
		l.Unlock()
		valid, err := l.isValid()
		if err != nil {
			continue
		}
		if !valid {
			l.Lock()
			l.held = false
			l.Unlock()
			return
		}
		conn := l.redis.UnblockedGet()
		ok, err := redis.Bool(conn.Do("PEXPIRE", l.name, "5000"))
		conn.Close()
		if err != nil {
			continue
		}
		if !ok {
			l.held = false
			return
		}
		time.Sleep(1 * time.Second)
	}
}

func (l *Lock) isValid() (bool, error) {
	conn := l.redis.UnblockedGet()
	defer conn.Close()
	value, err := redis.String(conn.Do("GET", l.name))
	if err != nil && err != redis.ErrNil {
		return false, err
	}
	if value != l.value {
		return false, nil
	}
	return true, nil
}

func (l *Lock) Release() {
	l.Lock()
	if l.held == false {
		l.Unlock()
		return
	}
	l.held = false
	l.Unlock()
	conn := l.redis.UnblockedGet()
	defer conn.Close()
	v, _ := redis.String(conn.Do("GET", l.name))
	if v == l.value {
		// Delete the key only if we are still the owner
		conn.Do("DEL", l.name)
	}
}

func (l *Lock) Held() bool {
	l.RLock()
	defer l.RUnlock()
	return l.held
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"os"
	"time"

	v3 "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/pkg/report"

	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"gopkg.in/cheggaaa/pb.v1"
)

// putCmd represents the put command
var putCmd = &cobra.Command{
	Use:   "put",
	Short: "Benchmark put",

	Run: putFunc,
}

var (
	keySize int
	valSize int

	putTotal int

	keySpaceSize int
	seqKeys      bool

	compactInterval   time.Duration
	compactIndexDelta int64
)

func init() {
	RootCmd.AddCommand(putCmd)
	putCmd.Flags().IntVar(&keySize, "key-size", 8, "Key size of put request")
	putCmd.Flags().IntVar(&valSize, "val-size", 8, "Value size of put request")
	putCmd.Flags().IntVar(&putTotal, "total", 10000, "Total number of put requests")
	putCmd.Flags().IntVar(&keySpaceSize, "key-space-size", 1, "Maximum possible keys")
	putCmd.Flags().BoolVar(&seqKeys, "sequential-keys", false, "Use sequential keys")
	putCmd.Flags().DurationVar(&compactInterval, "compact-interval", 0, `Interval to compact database (do not duplicate this with etcd's 'auto-compaction-retention' flag) (e.g. --compact-interval=5m compacts every 5-minute)`)
	putCmd.Flags().Int64Var(&compactIndexDelta, "compact-index-delta", 1000, "Delta between current revision and compact revision (e.g. current revision 10000, compact at 9000)")
}

func putFunc(cmd *cobra.Command, args []string) {
	if keySpaceSize <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", keySpaceSize)
		os.Exit(1)
	}

	requests := make(chan v3.Op, totalClients)
	clients := mustCreateClients(totalClients, totalConns)
	k, v := make([]byte, keySize), string(mustRandBytes(valSize))

	bar = pb.New(putTotal)
	bar.Format("Bom !")
	bar.Start()

	r := newReport()
	for i := range clients {
		wg.Add(1)
		go func(c *v3.Client) {
			defer wg.Done()
			for op := range requests {
				st := time.Now()
				_, err := c.Do(context.Background(), op)
				r.Results() <- report.Result{Err: err, Start: st, End: time.Now()}
				bar.Increment()
			}
		}(clients[i])
	}

	go func() {
		for i := 0; i < putTotal; i++ {
			if seqKeys {
				binary.PutVarint(k, int64(i%keySpaceSize))
			} else {
				binary.PutVarint(k, int64(rand.Intn(keySpaceSize)))
			}
			requests <- v3.OpPut(string(k), v)
		}
		close(requests)
	}()

	if compactInterval > 0 {
		go func() {
			for {
				time.Sleep(compactInterval)
				compactKV(clients)
			}
		}()
	}

	rc := r.Run()
	wg.Wait()
	close(r.Results())
	bar.Finish()
	fmt.Println(<-rc)
}

func compactKV(clients []*v3.Client) {
	var curRev int64
	for _, c := range clients {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		resp, err := c.KV.Get(ctx, "foo")
		cancel()
		if err != nil {
			panic(err)
		}
		curRev = resp.Header.Revision
		break
	}
	revToCompact := max(0, curRev-compactIndexDelta)
	for _, c := range clients {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		_, err := c.KV.Compact(ctx, revToCompact)
		cancel()
		if err != nil {
			panic(err)
		}
		break
	}
}

func max(n1, n2 int64) int64 {
	if n1 > n2 {
		return n1
	}
	return n2
}
<commit_msg>benchmark: add rate limit<commit_after>// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"encoding/binary"
	"fmt"
	"math"
	"math/rand"
	"os"
	"time"

	v3 "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/pkg/report"

	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"golang.org/x/time/rate"
	"gopkg.in/cheggaaa/pb.v1"
)

// putCmd represents the put command
var putCmd = &cobra.Command{
	Use:   "put",
	Short: "Benchmark put",

	Run: putFunc,
}

var (
	keySize int
	valSize int

	putTotal int
	putRate  int

	keySpaceSize int
	seqKeys      bool

	compactInterval   time.Duration
	compactIndexDelta int64
)

func init() {
	RootCmd.AddCommand(putCmd)
	putCmd.Flags().IntVar(&keySize, "key-size", 8, "Key size of put request")
	putCmd.Flags().IntVar(&valSize, "val-size", 8, "Value size of put request")
	putCmd.Flags().IntVar(&putRate, "rate", 0, "Maximum puts per second (0 is no limit)")

	putCmd.Flags().IntVar(&putTotal, "total", 10000, "Total number of put requests")
	putCmd.Flags().IntVar(&keySpaceSize, "key-space-size", 1, "Maximum possible keys")
	putCmd.Flags().BoolVar(&seqKeys, "sequential-keys", false, "Use sequential keys")
	putCmd.Flags().DurationVar(&compactInterval, "compact-interval", 0, `Interval to compact database (do not duplicate this with etcd's 'auto-compaction-retention' flag) (e.g. --compact-interval=5m compacts every 5-minute)`)
	putCmd.Flags().Int64Var(&compactIndexDelta, "compact-index-delta", 1000, "Delta between current revision and compact revision (e.g. current revision 10000, compact at 9000)")
}

func putFunc(cmd *cobra.Command, args []string) {
	if keySpaceSize <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", keySpaceSize)
		os.Exit(1)
	}

	requests := make(chan v3.Op, totalClients)
	if putRate == 0 {
		putRate = math.MaxInt32
	}
	limit := rate.NewLimiter(rate.Limit(putRate), 1)
	clients := mustCreateClients(totalClients, totalConns)
	k, v := make([]byte, keySize), string(mustRandBytes(valSize))

	bar = pb.New(putTotal)
	bar.Format("Bom !")
	bar.Start()

	r := newReport()
	for i := range clients {
		wg.Add(1)
		go func(c *v3.Client) {
			defer wg.Done()
			for op := range requests {
				limit.Wait(context.Background())

				st := time.Now()
				_, err := c.Do(context.Background(), op)
				r.Results() <- report.Result{Err: err, Start: st, End: time.Now()}
				bar.Increment()
			}
		}(clients[i])
	}

	go func() {
		for i := 0; i < putTotal; i++ {
			if seqKeys {
				binary.PutVarint(k, int64(i%keySpaceSize))
			} else {
				binary.PutVarint(k, int64(rand.Intn(keySpaceSize)))
			}
			requests <- v3.OpPut(string(k), v)
		}
		close(requests)
	}()

	if compactInterval > 0 {
		go func() {
			for {
				time.Sleep(compactInterval)
				compactKV(clients)
			}
		}()
	}

	rc := r.Run()
	wg.Wait()
	close(r.Results())
	bar.Finish()
	fmt.Println(<-rc)
}

func compactKV(clients []*v3.Client) {
	var curRev int64
	for _, c := range clients {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		resp, err := c.KV.Get(ctx, "foo")
		cancel()
		if err != nil {
			panic(err)
		}
		curRev = resp.Header.Revision
		break
	}
	revToCompact := max(0, curRev-compactIndexDelta)
	for _, c := range clients {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		_, err := c.KV.Compact(ctx, revToCompact)
		cancel()
		if err != nil {
			panic(err)
		}
		break
	}
}

func max(n1, n2 int64) int64 {
	if n1 > n2 {
		return n1
	}
	return n2
}
<|endoftext|>


<commit_before>package markdown

import (
	"fmt"
	"strings"
	"sync"
	"testing"
	"time"
)

func TestWatcher(t *testing.T) {
	expected := "12345678"
	interval := time.Millisecond * 100
	i := 0
	out := ""
	stopChan := TickerFunc(interval, func() {
		i++
		out += fmt.Sprint(i)
	})
	// wait little more because of concurrency
	time.Sleep(interval * 9)
	stopChan <- struct{}{}
	if !strings.HasPrefix(out, expected) {
		t.Fatalf("Expected to have prefix %v, found %v", expected, out)
	}
	out = ""
	i = 0
	var mu sync.Mutex
	stopChan = TickerFunc(interval, func() {
		i++
		mu.Lock()
		out += fmt.Sprint(i)
		mu.Unlock()
	})
	time.Sleep(interval * 10)
	mu.Lock()
	res := out
	mu.Unlock()
	if !strings.HasPrefix(res, expected) || res == expected {
		t.Fatalf("expected (%v) must be a proper prefix of out(%v).", expected, out)
	}
}
<commit_msg>Fix race condition on AppVeyor. Increase timeout a bit.<commit_after>package markdown

import (
	"fmt"
	"strings"
	"sync"
	"testing"
	"time"
)

func TestWatcher(t *testing.T) {
	expected := "12345678"
	interval := time.Millisecond * 100
	i := 0
	out := ""
	stopChan := TickerFunc(interval, func() {
		i++
		out += fmt.Sprint(i)
	})
	// wait little more because of concurrency
	time.Sleep(interval * 12)
	stopChan <- struct{}{}
	if !strings.HasPrefix(out, expected) {
		t.Fatalf("Expected to have prefix %v, found %v", expected, out)
	}
	out = ""
	i = 0
	var mu sync.Mutex
	stopChan = TickerFunc(interval, func() {
		i++
		mu.Lock()
		out += fmt.Sprint(i)
		mu.Unlock()
	})
	time.Sleep(interval * 15)
	mu.Lock()
	res := out
	mu.Unlock()
	if !strings.HasPrefix(res, expected) || res == expected {
		t.Fatalf("expected (%v) must be a proper prefix of out(%v).", expected, out)
	}
}
<|endoftext|>


<commit_before>package core

import "testing"

func TestVtoFS(t *testing.T) {
	_, value := VtoFS(Value(int32(4)))
	if value.(float64) != float64(4) {
		t.Error("err int32")
	}
	_, value = VtoFS(Value(float32(4)))
	if value.(float64) != float64(4) {
		t.Error("err float32")
	}
	_, value = VtoFS(Value(uint(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint")
	}
	_, value = VtoFS(Value(uint64(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint64")
	}
	_, value = VtoFS(Value(uint32(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint32")
	}
	_, value = VtoFS(Value(true))
	if value.(float64) != float64(1) {
		t.Error("err bool")
	}

}
<commit_msg>add-test<commit_after>package core

import "testing"

func TestVtoFS(t *testing.T) {
	_, value := VtoFS(Value(int32(4)))
	if value.(float64) != float64(4) {
		t.Error("err int32")
	}
	_, value = VtoFS(Value(float32(4)))
	if value.(float64) != float64(4) {
		t.Error("err float32")
	}
	_, value = VtoFS(Value(uint(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint")
	}
	_, value = VtoFS(Value(uint64(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint64")
	}
	_, value = VtoFS(Value(uint32(4)))
	if value.(float64) != float64(4) {
		t.Error("err uint32")
	}
	_, value = VtoFS(Value(true))
	if value.(float64) != float64(1) {
		t.Error("err bool")
	}
	_, value = VtoFS(Value(false))
	if value.(float64) != float64(0) {
		t.Error("err bool")
	}

}
<|endoftext|>


<commit_before>package cld

import (
	"strings"
)

// Dot 形式の文字列に変換できることを表すインターフェース
type Dot interface {
	// Dot 形式の文字列を返却する
	ToDot() string
}

// フィールド
type Field struct {
	field string
}

// フィールドを作成する
func CreateFieldFromString(def string) Field {
	return Field{def}
}

// Dot 形式の文字列を返却する
func (this Field) ToDot() string {
	return this.field + "\\l"
}

// フィールドリスト
type Fields struct {
	fields []Field
}

// フィールドリストを作成する
func CreateFieldsFromStrings(defs []string) Fields {
	// 必要な長さのスライスを作成
	fields := make([]Field, len(defs))

	// スライスにフィールド定義を格納
	for i, v := range defs {
		fields[i] = CreateFieldFromString(v)
	}

	// Fields 返却
	return Fields{fields}
}

// Dot 形式の文字列を返却する
func (this Fields) ToDot() string {
	// 必要な長さのスライスを作成
	defs := make([]string, len(this.fields))

	// スライスにフィールド定義を格納
	for i, v := range this.fields {
		defs[i] = v.ToDot()
	}

	// Fields 返却
	return strings.Join(defs, "")
}

// メソッド
type Method struct {
	method string
}

// メソッドを作成する
func CreateMethodFromString(def string) Method {
	return Method{def}
}

// Dot 形式の文字列を返却する
func (this Method) ToDot() string {
	return this.method + "\\l"
}

// メソッドリスト
type Methods struct {
	methods []Method
}

// メソッドリストを作成する
func CreateMethodsFromStrings(defs []string) Methods {
	// 必要な長さのスライスを作成
	methods := make([]Method, len(defs))

	// スライスにフィールド定義を格納
	for i, v := range defs {
		methods[i] = CreateMethodFromString(v)
	}

	// Methods 返却
	return Methods{methods}
}

// Dot 形式の文字列を返却する
func (this Methods) ToDot() string {
	// 必要な長さのスライスを作成
	defs := make([]string, len(this.methods))

	// スライスにフィールド定義を格納
	for i, v := range this.methods {
		defs[i] = v.ToDot()
	}

	// Fields 返却
	return strings.Join(defs, "")
}

// クラス
type Class struct {
	stereotype string
	name       string
	fields     Fields
	methods    Methods
}

// 文字列からクラスを作成する
func CreateClassFromDefs(stereotype string, name string, fieldDefs []string, methodDefs []string) Class {
	fields := CreateFieldsFromStrings(fieldDefs)
	methods := CreateMethodsFromStrings(methodDefs)
	return Class{stereotype, name, fields, methods}
}

// Dot 形式の文字列を返却する
func (this Class) ToDot() string {
	// 必要な長さのスライスを作成
	defs := []string{this.name, " [label = \"{"}

	if this.stereotype != "" {
		defs = append(defs, "\\<\\<", this.stereotype, "\\>\\>\\n")
	}

	defs = append(defs, this.name, "|", this.fields.ToDot(), "|", this.methods.ToDot(), "}\"];")

	// 文字列返却
	return strings.Join(defs, "")
}

// 名前空間(パッケージ)
type Namespace struct {
	name    string
	classes []Class
}

// Namespace を作成する
func CreateNamespace(name string, classes []Class) Namespace {
	return Namespace{name, classes}
}

// Dot 形式の文字列を返却する
func (this Namespace) ToDot() string {
	defs := []string{"subgraph cluster_" + this.name + " {"}

	defs = append(defs, "label = \""+this.name+"\";")

	for _, v := range this.classes {
		defs = append(defs, v.ToDot())
	}

	defs = append(defs, "}")

	return strings.Join(defs, "\n")
}

type RelationType int

// 関係
type Relation struct {
	name             string
	relationType     RelationType
	fromClassName    string
	toClassName      string
	fromMultiplicity string
	toMultiplicity   string
}

// Relation を作成する
func CreateRelation(name string, relationType RelationType, fromClassName string, toClassName string, fromMultiplicity string, toMultiplicity string) Relation {
	return Relation{name, relationType, fromClassName, toClassName, fromMultiplicity, toMultiplicity}
}

// 関係の種類
const (
	RELATION_NORMAL = iota
	RELATION_INHERIT
	RELATION_IMPLEMENT
	RELATION_AGGREGATION
	RELATION_COMPOSITION
)

func (this Relation) getEdgeStyles() (style string, arrowhead string) {

	if this.relationType == RELATION_INHERIT {
		style = "solid"
		arrowhead = "onormal"
	} else if this.relationType == RELATION_IMPLEMENT {
		style = "dashed"
		arrowhead = "onormal"
	} else if this.relationType == RELATION_AGGREGATION {
		style = "solid"
		arrowhead = ""
	} else if this.relationType == RELATION_COMPOSITION {
		style = "solid"
		arrowhead = ""
	}

	return style, arrowhead
}

// Dot 形式の文字列を返却する
func (this Relation) ToDot() string {

	style, arrowhead := this.getEdgeStyles()

	// 基本
	base := []string{"edge [style = \"" + style + "\", arrowhead = \"" + arrowhead + "\"];\n"}
	base = append(base, this.fromClassName+" -> "+this.toClassName)

	// 詳細
	detail := []string{}

	// 関係名名前
	if this.name != "" {
		detail = append(detail, "label = \""+this.name+"\"")
	}

	// FROM の処理
	if this.fromMultiplicity != "" {
		detail = append(detail, "taillabel = \""+this.fromMultiplicity+"\"")
	}

	// TO の処理
	if this.toMultiplicity != "" {
		detail = append(detail, "headlabel = \""+this.toMultiplicity+"\"")
	}

	var detailString string
	if len(detail) != 0 {
		detailString = "[" + strings.Join(detail, ",") + "];"
	} else {
		detailString = ""
	}

	return strings.Join(base, "") + detailString
}

// クラス図
type ClassDiagram struct {
	name       string
	namespaces []Namespace
	classes    []Class
	relations  []Relation
}

// クラス図作成
func CreateClassDiagram(name string, namespaces []Namespace, classes []Class, relations []Relation) ClassDiagram {
	return ClassDiagram{name, namespaces, classes, relations}
}

// Dot 形式の文字列を返却する
func (this ClassDiagram) ToDot() string {

	defs := []string{"digraph " + this.name + " {\nnode [shape = record];\n"}

	for _, v := range this.namespaces {
		defs = append(defs, v.ToDot())
	}

	for _, v := range this.classes {
		defs = append(defs, v.ToDot())
	}

	for _, v := range this.relations {
		defs = append(defs, v.ToDot())
	}

	defs = append(defs, "}")

	return strings.Join(defs, "\n")
}
<commit_msg>Fixed package name and Relation's type.<commit_after>package ozcld

import (
	"strings"
)

// Dot 形式の文字列に変換できることを表すインターフェース
type Dot interface {
	// Dot 形式の文字列を返却する
	ToDot() string
}

// フィールド
type Field struct {
	field string
}

// フィールドを作成する
func CreateFieldFromString(def string) Field {
	return Field{def}
}

// Dot 形式の文字列を返却する
func (this Field) ToDot() string {
	return this.field + "\\l"
}

// フィールドリスト
type Fields struct {
	fields []Field
}

// フィールドリストを作成する
func CreateFieldsFromStrings(defs []string) Fields {
	// 必要な長さのスライスを作成
	fields := make([]Field, len(defs))

	// スライスにフィールド定義を格納
	for i, v := range defs {
		fields[i] = CreateFieldFromString(v)
	}

	// Fields 返却
	return Fields{fields}
}

// Dot 形式の文字列を返却する
func (this Fields) ToDot() string {
	// 必要な長さのスライスを作成
	defs := make([]string, len(this.fields))

	// スライスにフィールド定義を格納
	for i, v := range this.fields {
		defs[i] = v.ToDot()
	}

	// Fields 返却
	return strings.Join(defs, "")
}

// メソッド
type Method struct {
	method string
}

// メソッドを作成する
func CreateMethodFromString(def string) Method {
	return Method{def}
}

// Dot 形式の文字列を返却する
func (this Method) ToDot() string {
	return this.method + "\\l"
}

// メソッドリスト
type Methods struct {
	methods []Method
}

// メソッドリストを作成する
func CreateMethodsFromStrings(defs []string) Methods {
	// 必要な長さのスライスを作成
	methods := make([]Method, len(defs))

	// スライスにフィールド定義を格納
	for i, v := range defs {
		methods[i] = CreateMethodFromString(v)
	}

	// Methods 返却
	return Methods{methods}
}

// Dot 形式の文字列を返却する
func (this Methods) ToDot() string {
	// 必要な長さのスライスを作成
	defs := make([]string, len(this.methods))

	// スライスにフィールド定義を格納
	for i, v := range this.methods {
		defs[i] = v.ToDot()
	}

	// Fields 返却
	return strings.Join(defs, "")
}

// クラス
type Class struct {
	stereotype string
	name       string
	fields     Fields
	methods    Methods
}

// 文字列からクラスを作成する
func CreateClassFromDefs(stereotype string, name string, fieldDefs []string, methodDefs []string) Class {
	fields := CreateFieldsFromStrings(fieldDefs)
	methods := CreateMethodsFromStrings(methodDefs)
	return Class{stereotype, name, fields, methods}
}

// Dot 形式の文字列を返却する
func (this Class) ToDot() string {
	// 必要な長さのスライスを作成
	defs := []string{this.name, " [label = \"{"}

	if this.stereotype != "" {
		defs = append(defs, "\\<\\<", this.stereotype, "\\>\\>\\n")
	}

	defs = append(defs, this.name, "|", this.fields.ToDot(), "|", this.methods.ToDot(), "}\"];")

	// 文字列返却
	return strings.Join(defs, "")
}

// 名前空間(パッケージ)
type Namespace struct {
	name    string
	classes []Class
}

// Namespace を作成する
func CreateNamespace(name string, classes []Class) Namespace {
	return Namespace{name, classes}
}

// Dot 形式の文字列を返却する
func (this Namespace) ToDot() string {
	defs := []string{"subgraph cluster_" + this.name + " {"}

	defs = append(defs, "label = \""+this.name+"\";")

	for _, v := range this.classes {
		defs = append(defs, v.ToDot())
	}

	defs = append(defs, "}")

	return strings.Join(defs, "\n")
}

type RelationType int

// 関係
type Relation struct {
	name             string
	relationType     RelationType
	fromClassName    string
	toClassName      string
	fromMultiplicity string
	toMultiplicity   string
}

// Relation を作成する
func CreateRelation(name string, relationType RelationType, fromClassName string, toClassName string, fromMultiplicity string, toMultiplicity string) Relation {
	return Relation{name, relationType, fromClassName, toClassName, fromMultiplicity, toMultiplicity}
}

// 関係の種類
const (
	RELATION_NORMAL RelationType = iota
	RELATION_INHERIT
	RELATION_IMPLEMENT
	RELATION_AGGREGATION
	RELATION_COMPOSITION
)

func (this Relation) getEdgeStyles() (style string, arrowhead string) {

	if this.relationType == RELATION_INHERIT {
		style = "solid"
		arrowhead = "onormal"
	} else if this.relationType == RELATION_IMPLEMENT {
		style = "dashed"
		arrowhead = "onormal"
	} else if this.relationType == RELATION_AGGREGATION {
		style = "solid"
		arrowhead = ""
	} else if this.relationType == RELATION_COMPOSITION {
		style = "solid"
		arrowhead = ""
	}

	return style, arrowhead
}

// Dot 形式の文字列を返却する
func (this Relation) ToDot() string {

	style, arrowhead := this.getEdgeStyles()

	// 基本
	base := []string{"edge [style = \"" + style + "\", arrowhead = \"" + arrowhead + "\"];\n"}
	base = append(base, this.fromClassName+" -> "+this.toClassName)

	// 詳細
	detail := []string{}

	// 関係名名前
	if this.name != "" {
		detail = append(detail, "label = \""+this.name+"\"")
	}

	// FROM の処理
	if this.fromMultiplicity != "" {
		detail = append(detail, "taillabel = \""+this.fromMultiplicity+"\"")
	}

	// TO の処理
	if this.toMultiplicity != "" {
		detail = append(detail, "headlabel = \""+this.toMultiplicity+"\"")
	}

	var detailString string
	if len(detail) != 0 {
		detailString = "[" + strings.Join(detail, ",") + "];"
	} else {
		detailString = ""
	}

	return strings.Join(base, "") + detailString
}

// クラス図
type ClassDiagram struct {
	name       string
	namespaces []Namespace
	classes    []Class
	relations  []Relation
}

// クラス図作成
func CreateClassDiagram(name string, namespaces []Namespace, classes []Class, relations []Relation) ClassDiagram {
	return ClassDiagram{name, namespaces, classes, relations}
}

// Dot 形式の文字列を返却する
func (this ClassDiagram) ToDot() string {

	defs := []string{"digraph " + this.name + " {\nnode [shape = record];\n"}

	for _, v := range this.namespaces {
		defs = append(defs, v.ToDot())
	}

	for _, v := range this.classes {
		defs = append(defs, v.ToDot())
	}

	for _, v := range this.relations {
		defs = append(defs, v.ToDot())
	}

	defs = append(defs, "}")

	return strings.Join(defs, "\n")
}
<|endoftext|>