<commit_before>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * 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 objectstorage

import (
	"bytes"
	"encoding/hex"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// operation - rest operation
type operation struct {
	HTTPServer string
	HTTPMethod string
	HTTPPath   string
}

// request - a http request
type request struct {
	req    *http.Request
	config *Config
	body   io.ReadSeeker
	region string
}

const (
	authHeaderPrefix = "AWS4-HMAC-SHA256"
	iso8601Format    = "20060102T150405Z"
	yyyymmdd         = "20060102"
)

var ignoredHeaders = map[string]bool{
	"Authorization":  true,
	"Content-Type":   true,
	"Content-Length": true,
	"User-Agent":     true,
}

// newRequest - instantiate a new request
func newRequest(op *operation, config *Config, body io.ReadSeeker) (*request, error) {
	// if no method default to POST
	method := op.HTTPMethod
	if method == "" {
		method = "POST"
	}

	// parse URL for the combination of HTTPServer + HTTPPath
	u, err := url.Parse(op.HTTPServer + op.HTTPPath)
	if err != nil {
		return nil, err
	}

	// get a new HTTP request, for the requested method
	req, err := http.NewRequest(method, u.String(), nil)
	if err != nil {
		return nil, err
	}

	// set UserAgent
	req.Header.Set("User-Agent", config.userAgent)

	// set ContentType, if available
	if config.ContentType != "" {
		req.Header.Set("Content-Type", config.ContentType)
	}

	// add body
	switch {
	case body == nil:
		req.Body = nil
	default:
		req.Body = ioutil.NopCloser(body)
	}

	// save for subsequent use
	r := new(request)
	r.config = config
	r.req = req
	r.body = body

	return r, nil
}

// Do - start the request
func (r *request) Do() (resp *http.Response, err error) {
	r.SignV4()
	client := &http.Client{}
	return client.Do(r.req)
}

// Set - set additional headers if any
func (r *request) Set(key, value string) {
	r.req.Header.Set(key, value)
}

// Get - get header values
func (r *request) Get(key string) string {
	return r.req.Header.Get(key)
}

// SignV4 the request before Do() (version 4.0)
func (r *request) SignV4() {
	t := time.Now().UTC()
	// Add date if not present
	if date := r.Get("Date"); date == "" {
		r.Set("X-Amz-Date", t.Format(iso8601Format))
	}

	hash := func() string {
		switch {
		case r.body == nil:
			return hex.EncodeToString(sum256([]byte{}))
		default:
			sum256Bytes, _ := sum256Reader(r.body)
			return hex.EncodeToString(sum256Bytes)
		}
	}
	bodyDigest := hash()
	r.req.Header.Add("X-Amz-Content-Sha256", bodyDigest)

	canonicalHeaders := func() string {
		var headers []string
		vals := make(map[string][]string)
		for k, vv := range r.req.Header {
			if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
				continue // ignored header
			}
			headers = append(headers, strings.ToLower(k))
			vals[strings.ToLower(k)] = vv
		}
		headers = append(headers, "host")
		sort.Strings(headers)

		var buf bytes.Buffer
		for _, k := range headers {
			buf.WriteString(k)
			buf.WriteByte(':')
			switch {
			case k == "host":
				buf.WriteString(r.req.URL.Host)
				fallthrough
			default:
				for idx, v := range vals[k] {
					if idx > 0 {
						buf.WriteByte(',')
					}
					buf.WriteString(v)
				}
				buf.WriteByte('\n')
			}
		}
		return buf.String()
	}

	signedHeaders := func() string {
		var headers []string
		for k := range r.req.Header {
			if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
				continue // ignored header
			}
			headers = append(headers, strings.ToLower(k))
		}
		headers = append(headers, "host")
		sort.Strings(headers)
		return strings.Join(headers, ";")
	}

	canonicalRequest := func() string {
		r.req.URL.RawQuery = strings.Replace(r.req.URL.Query().Encode(), "+", "%20", -1)
		canonicalRequest := strings.Join([]string{
			r.req.Method,
			r.req.URL.Path,
			r.req.URL.RawQuery,
			canonicalHeaders(),
			signedHeaders(),
			bodyDigest,
		}, "\n")
		return canonicalRequest
	}

	scope := strings.Join([]string{
		t.Format(yyyymmdd),
		"us-east-1",
		"s3",
		"aws4_request",
	}, "/")

	stringToSign := func() string {
		stringToSign := authHeaderPrefix + "\n" + t.Format(iso8601Format) + "\n"
		stringToSign = stringToSign + scope + "\n"
		stringToSign = stringToSign + hex.EncodeToString(sum256([]byte(canonicalRequest())))
		return stringToSign
	}

	signingKey := func() []byte {
		secret := r.config.SecretAccessKey
		date := sumHMAC([]byte("AWS4"+secret), []byte(t.Format(yyyymmdd)))
		region := sumHMAC(date, []byte("us-east-1"))
		service := sumHMAC(region, []byte("s3"))
		signingKey := sumHMAC(service, []byte("aws4_request"))
		return signingKey
	}

	signature := func() string {
		return hex.EncodeToString(sumHMAC(signingKey(), []byte(stringToSign())))
	}

	parts := []string{authHeaderPrefix + " Credential=" + r.config.AccessKeyID + "/" + scope,
		"SignedHeaders=" + signedHeaders(), "Signature=" + signature(),
	}
	auth := strings.Join(parts, ", ")
	r.Set("Authorization", auth)
}
<commit_msg>If accesskey and secretaccess are not provided passdown the request without signature<commit_after>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * 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 objectstorage

import (
	"bytes"
	"encoding/hex"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// operation - rest operation
type operation struct {
	HTTPServer string
	HTTPMethod string
	HTTPPath   string
}

// request - a http request
type request struct {
	req    *http.Request
	config *Config
	body   io.ReadSeeker
	region string
}

const (
	authHeaderPrefix = "AWS4-HMAC-SHA256"
	iso8601Format    = "20060102T150405Z"
	yyyymmdd         = "20060102"
)

var ignoredHeaders = map[string]bool{
	"Authorization":  true,
	"Content-Type":   true,
	"Content-Length": true,
	"User-Agent":     true,
}

// newRequest - instantiate a new request
func newRequest(op *operation, config *Config, body io.ReadSeeker) (*request, error) {
	// if no method default to POST
	method := op.HTTPMethod
	if method == "" {
		method = "POST"
	}

	// parse URL for the combination of HTTPServer + HTTPPath
	u, err := url.Parse(op.HTTPServer + op.HTTPPath)
	if err != nil {
		return nil, err
	}

	// get a new HTTP request, for the requested method
	req, err := http.NewRequest(method, u.String(), nil)
	if err != nil {
		return nil, err
	}

	// set UserAgent
	req.Header.Set("User-Agent", config.userAgent)

	// set ContentType, if available
	if config.ContentType != "" {
		req.Header.Set("Content-Type", config.ContentType)
	}

	// add body
	switch {
	case body == nil:
		req.Body = nil
	default:
		req.Body = ioutil.NopCloser(body)
	}

	// save for subsequent use
	r := new(request)
	r.config = config
	r.req = req
	r.body = body

	return r, nil
}

// Do - start the request
func (r *request) Do() (resp *http.Response, err error) {
	if r.config.AccessKeyID != "" && r.config.SecretAccessKey != "" {
		r.SignV4()
	}
	client := &http.Client{}
	return client.Do(r.req)
}

// Set - set additional headers if any
func (r *request) Set(key, value string) {
	r.req.Header.Set(key, value)
}

// Get - get header values
func (r *request) Get(key string) string {
	return r.req.Header.Get(key)
}

// SignV4 the request before Do() (version 4.0)
func (r *request) SignV4() {
	t := time.Now().UTC()
	// Add date if not present
	if date := r.Get("Date"); date == "" {
		r.Set("X-Amz-Date", t.Format(iso8601Format))
	}

	hash := func() string {
		switch {
		case r.body == nil:
			return hex.EncodeToString(sum256([]byte{}))
		default:
			sum256Bytes, _ := sum256Reader(r.body)
			return hex.EncodeToString(sum256Bytes)
		}
	}
	bodyDigest := hash()
	r.req.Header.Add("X-Amz-Content-Sha256", bodyDigest)

	canonicalHeaders := func() string {
		var headers []string
		vals := make(map[string][]string)
		for k, vv := range r.req.Header {
			if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
				continue // ignored header
			}
			headers = append(headers, strings.ToLower(k))
			vals[strings.ToLower(k)] = vv
		}
		headers = append(headers, "host")
		sort.Strings(headers)

		var buf bytes.Buffer
		for _, k := range headers {
			buf.WriteString(k)
			buf.WriteByte(':')
			switch {
			case k == "host":
				buf.WriteString(r.req.URL.Host)
				fallthrough
			default:
				for idx, v := range vals[k] {
					if idx > 0 {
						buf.WriteByte(',')
					}
					buf.WriteString(v)
				}
				buf.WriteByte('\n')
			}
		}
		return buf.String()
	}

	signedHeaders := func() string {
		var headers []string
		for k := range r.req.Header {
			if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
				continue // ignored header
			}
			headers = append(headers, strings.ToLower(k))
		}
		headers = append(headers, "host")
		sort.Strings(headers)
		return strings.Join(headers, ";")
	}

	canonicalRequest := func() string {
		r.req.URL.RawQuery = strings.Replace(r.req.URL.Query().Encode(), "+", "%20", -1)
		canonicalRequest := strings.Join([]string{
			r.req.Method,
			r.req.URL.Path,
			r.req.URL.RawQuery,
			canonicalHeaders(),
			signedHeaders(),
			bodyDigest,
		}, "\n")
		return canonicalRequest
	}

	scope := strings.Join([]string{
		t.Format(yyyymmdd),
		"us-east-1",
		"s3",
		"aws4_request",
	}, "/")

	stringToSign := func() string {
		stringToSign := authHeaderPrefix + "\n" + t.Format(iso8601Format) + "\n"
		stringToSign = stringToSign + scope + "\n"
		stringToSign = stringToSign + hex.EncodeToString(sum256([]byte(canonicalRequest())))
		return stringToSign
	}

	signingKey := func() []byte {
		secret := r.config.SecretAccessKey
		date := sumHMAC([]byte("AWS4"+secret), []byte(t.Format(yyyymmdd)))
		region := sumHMAC(date, []byte("us-east-1"))
		service := sumHMAC(region, []byte("s3"))
		signingKey := sumHMAC(service, []byte("aws4_request"))
		return signingKey
	}

	signature := func() string {
		return hex.EncodeToString(sumHMAC(signingKey(), []byte(stringToSign())))
	}

	parts := []string{authHeaderPrefix + " Credential=" + r.config.AccessKeyID + "/" + scope,
		"SignedHeaders=" + signedHeaders(), "Signature=" + signature(),
	}
	auth := strings.Join(parts, ", ")
	r.Set("Authorization", auth)
}
<|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 cli

import (
	"fmt"
	"github.com/spf13/cobra"

	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/util"
)

func runRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	TryGetProject()

	b, err := TargetBuilderForTargetOrUnittest(args[0])
	if err != nil {
		NewtUsage(cmd, err)
	}

	testPkg := b.GetTestPkg()
	if testPkg != nil {
		b.InjectSetting("TESTUTIL_SYSTEM_ASSERT", "1")
		if err := b.SelfTestCreateExe(); err != nil {
			NewtUsage(nil, err)
		}
		if err := b.SelfTestDebug(); err != nil {
			NewtUsage(nil, err)
		}
	} else {
		var version string = ""
		if len(args) > 1 {
			version = args[1]
		} else {
			// If user did not provide version number and
			// the target is not a bootloader, then ask
			// the tell user to enter a version or use
			// 0 for default value.

			// Resolve to get the config values.
			res, err := b.Resolve()
			if err != nil {
				NewtUsage(nil, err)
			}
			features := res.Cfg.Features()

			if _, loader := features["BOOT_LOADER"]; !loader {
				version = "0"
				fmt.Println("Enter image version(default 0):")
				fmt.Scanf("%s\n", &version)
			}
		}
		if err := b.Build(); err != nil {
			NewtUsage(nil, err)
		}

		if len(version) > 0 {
			_, _, err = b.CreateImages(version, "", 0)
			if err != nil {
				NewtUsage(cmd, err)
			}

		}

		if err := b.Load(extraJtagCmd); err != nil {
			NewtUsage(nil, err)
		}

		if err := b.Debug(extraJtagCmd, true, noGDB_flag); err != nil {
			NewtUsage(nil, err)
		}
	}
}

func AddRunCommands(cmd *cobra.Command) {
	runHelpText := "Same as running\n" +
		" - build <target>\n" +
		" - create-image <target> <version>\n" +
		" - load <target>\n" +
		" - debug <target>\n\n" +
		"Note if version number is omitted, create-image step is skipped\n"
	runHelpEx := "  newt run <target-name> [<version>]\n"

	runCmd := &cobra.Command{
		Use:     "run",
		Short:   "build/create-image/download/debug <target>",
		Long:    runHelpText,
		Example: runHelpEx,
		Run:     runRunCmd,
	}

	runCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "", "",
		"Extra commands to send to JTAG software")
	runCmd.PersistentFlags().BoolVarP(&noGDB_flag, "noGDB", "n", false,
		"Do not start GDB from command line")
	runCmd.PersistentFlags().BoolVarP(&newtutil.NewtForce,
		"force", "f", false,
		"Ignore flash overflow errors during image creation")

	cmd.AddCommand(runCmd)
	AddTabCompleteFn(runCmd, func() []string {
		return append(targetList(), unittestList()...)
	})
}
<commit_msg>MYNEWT-778 newt - "newt run" fails for native tgts<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 cli

import (
	"fmt"
	"github.com/spf13/cobra"

	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/util"
)

func runRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	TryGetProject()

	b, err := TargetBuilderForTargetOrUnittest(args[0])
	if err != nil {
		NewtUsage(cmd, err)
	}

	testPkg := b.GetTestPkg()
	if testPkg != nil {
		b.InjectSetting("TESTUTIL_SYSTEM_ASSERT", "1")
		if err := b.SelfTestCreateExe(); err != nil {
			NewtUsage(nil, err)
		}
		if err := b.SelfTestDebug(); err != nil {
			NewtUsage(nil, err)
		}
	} else {
		var version string = ""
		if len(args) > 1 {
			version = args[1]
		} else {
			// If user did not provide version number and the target is not a
			// bootloader and doesn't run in the simulator, then ask the user
			// to enter a version or use 0 for default value.

			// Resolve to get the config values.
			res, err := b.Resolve()
			if err != nil {
				NewtUsage(nil, err)
			}
			features := res.Cfg.Features()

			if !features["BOOT_LOADER"] && !features["BSP_SIMULATED"] {
				version = "0"
				fmt.Println("Enter image version(default 0):")
				fmt.Scanf("%s\n", &version)
			}
		}
		if err := b.Build(); err != nil {
			NewtUsage(nil, err)
		}

		if len(version) > 0 {
			_, _, err = b.CreateImages(version, "", 0)
			if err != nil {
				NewtUsage(cmd, err)
			}

		}

		if err := b.Load(extraJtagCmd); err != nil {
			NewtUsage(nil, err)
		}

		if err := b.Debug(extraJtagCmd, true, noGDB_flag); err != nil {
			NewtUsage(nil, err)
		}
	}
}

func AddRunCommands(cmd *cobra.Command) {
	runHelpText := "Same as running\n" +
		" - build <target>\n" +
		" - create-image <target> <version>\n" +
		" - load <target>\n" +
		" - debug <target>\n\n" +
		"Note if version number is omitted, create-image step is skipped\n"
	runHelpEx := "  newt run <target-name> [<version>]\n"

	runCmd := &cobra.Command{
		Use:     "run",
		Short:   "build/create-image/download/debug <target>",
		Long:    runHelpText,
		Example: runHelpEx,
		Run:     runRunCmd,
	}

	runCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "", "",
		"Extra commands to send to JTAG software")
	runCmd.PersistentFlags().BoolVarP(&noGDB_flag, "noGDB", "n", false,
		"Do not start GDB from command line")
	runCmd.PersistentFlags().BoolVarP(&newtutil.NewtForce,
		"force", "f", false,
		"Ignore flash overflow errors during image creation")

	cmd.AddCommand(runCmd)
	AddTabCompleteFn(runCmd, func() []string {
		return append(targetList(), unittestList()...)
	})
}
<|endoftext|>


<commit_before>// Copyright 2016 TiKV Project 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"context"
	"net/http"
	"net/http/pprof"

	"github.com/gorilla/mux"
	"github.com/tikv/pd/server"
	"github.com/unrolled/render"
)

func createStreamingRender() *render.Render {
	return render.New(render.Options{
		StreamingJSON: true,
	})
}

func createIndentRender() *render.Render {
	return render.New(render.Options{
		IndentJSON: true,
	})
}

// The returned function is used as a lazy router to avoid the data race problem.
// @title Placement Driver Core API
// @version 1.0
// @description This is placement driver.
// @contact.name Placement Driver Support
// @contact.url https://github.com/tikv/pd/issues
// @contact.email info@pingcap.com
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @BasePath /pd/api/v1
func createRouter(ctx context.Context, prefix string, svr *server.Server) *mux.Router {
	rd := createIndentRender()

	rootRouter := mux.NewRouter().PathPrefix(prefix).Subrouter()
	handler := svr.GetHandler()

	apiRouter := rootRouter.PathPrefix("/api/v1").Subrouter()

	clusterRouter := apiRouter.NewRoute().Subrouter()
	clusterRouter.Use(newClusterMiddleware(svr).Middleware)

	operatorHandler := newOperatorHandler(handler, rd)
	apiRouter.HandleFunc("/operators", operatorHandler.List).Methods("GET")
	apiRouter.HandleFunc("/operators", operatorHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Delete).Methods("DELETE")

	schedulerHandler := newSchedulerHandler(svr, rd)
	apiRouter.HandleFunc("/schedulers", schedulerHandler.List).Methods("GET")
	apiRouter.HandleFunc("/schedulers", schedulerHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.Delete).Methods("DELETE")
	apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.PauseOrResume).Methods("POST")
	schedulerConfigHandler := newSchedulerConfigHandler(svr, rd)
	rootRouter.PathPrefix(server.SchedulerConfigHandlerPath).Handler(schedulerConfigHandler)

	clusterHandler := newClusterHandler(svr, rd)
	apiRouter.Handle("/cluster", clusterHandler).Methods("GET")
	apiRouter.HandleFunc("/cluster/status", clusterHandler.GetClusterStatus).Methods("GET")

	confHandler := newConfHandler(svr, rd)
	apiRouter.HandleFunc("/config", confHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/config", confHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/config/default", confHandler.GetDefault).Methods("GET")
	apiRouter.HandleFunc("/config/schedule", confHandler.GetSchedule).Methods("GET")
	apiRouter.HandleFunc("/config/schedule", confHandler.SetSchedule).Methods("POST")
	apiRouter.HandleFunc("/config/replicate", confHandler.GetReplication).Methods("GET")
	apiRouter.HandleFunc("/config/replicate", confHandler.SetReplication).Methods("POST")
	apiRouter.HandleFunc("/config/label-property", confHandler.GetLabelProperty).Methods("GET")
	apiRouter.HandleFunc("/config/label-property", confHandler.SetLabelProperty).Methods("POST")
	apiRouter.HandleFunc("/config/cluster-version", confHandler.GetClusterVersion).Methods("GET")
	apiRouter.HandleFunc("/config/cluster-version", confHandler.SetClusterVersion).Methods("POST")
	apiRouter.HandleFunc("/config/replication-mode", confHandler.GetReplicationMode).Methods("GET")
	apiRouter.HandleFunc("/config/replication-mode", confHandler.SetReplicationMode).Methods("POST")

	rulesHandler := newRulesHandler(svr, rd)
	clusterRouter.HandleFunc("/config/rules", rulesHandler.GetAll).Methods("GET")
	clusterRouter.HandleFunc("/config/rules", rulesHandler.SetAll).Methods("POST")
	clusterRouter.HandleFunc("/config/rules/batch", rulesHandler.Batch).Methods("POST")
	clusterRouter.HandleFunc("/config/rules/group/{group}", rulesHandler.GetAllByGroup).Methods("GET")
	clusterRouter.HandleFunc("/config/rules/region/{region}", rulesHandler.GetAllByRegion).Methods("GET")
	clusterRouter.HandleFunc("/config/rules/key/{key}", rulesHandler.GetAllByKey).Methods("GET")
	clusterRouter.HandleFunc("/config/rule/{group}/{id}", rulesHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/config/rule", rulesHandler.Set).Methods("POST")
	clusterRouter.HandleFunc("/config/rule/{group}/{id}", rulesHandler.Delete).Methods("DELETE")

	clusterRouter.HandleFunc("/config/rule_group/{id}", rulesHandler.GetGroupConfig).Methods("GET")
	clusterRouter.HandleFunc("/config/rule_group", rulesHandler.SetGroupConfig).Methods("POST")
	clusterRouter.HandleFunc("/config/rule_group/{id}", rulesHandler.DeleteGroupConfig).Methods("DELETE")
	clusterRouter.HandleFunc("/config/rule_groups", rulesHandler.GetAllGroupConfigs).Methods("GET")

	clusterRouter.HandleFunc("/config/placement-rule", rulesHandler.GetAllGroupBundles).Methods("GET")
	clusterRouter.HandleFunc("/config/placement-rule", rulesHandler.SetAllGroupBundles).Methods("POST")
	// {group} can be a regular expression, we should enable path encode to
	// support special characters.
	escapeRouter := clusterRouter.NewRoute().Subrouter().UseEncodedPath()
	clusterRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.GetGroupBundle).Methods("GET")
	clusterRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.SetGroupBundle).Methods("POST")
	escapeRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.DeleteGroupBundle).Methods("DELETE")

	storeHandler := newStoreHandler(handler, rd)
	clusterRouter.HandleFunc("/store/{id}", storeHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/store/{id}", storeHandler.Delete).Methods("DELETE")
	clusterRouter.HandleFunc("/store/{id}/state", storeHandler.SetState).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/label", storeHandler.SetLabels).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/weight", storeHandler.SetWeight).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/limit", storeHandler.SetLimit).Methods("POST")
	storesHandler := newStoresHandler(handler, rd)
	clusterRouter.Handle("/stores", storesHandler).Methods("GET")
	clusterRouter.HandleFunc("/stores/remove-tombstone", storesHandler.RemoveTombStone).Methods("DELETE")
	clusterRouter.HandleFunc("/stores/limit", storesHandler.GetAllLimit).Methods("GET")
	clusterRouter.HandleFunc("/stores/limit", storesHandler.SetAllLimit).Methods("POST")
	clusterRouter.HandleFunc("/stores/limit/scene", storesHandler.SetStoreLimitScene).Methods("POST")
	clusterRouter.HandleFunc("/stores/limit/scene", storesHandler.GetStoreLimitScene).Methods("GET")

	labelsHandler := newLabelsHandler(svr, rd)
	clusterRouter.HandleFunc("/labels", labelsHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/labels/stores", labelsHandler.GetStores).Methods("GET")

	hotStatusHandler := newHotStatusHandler(handler, rd)
	apiRouter.HandleFunc("/hotspot/regions/write", hotStatusHandler.GetHotWriteRegions).Methods("GET")
	apiRouter.HandleFunc("/hotspot/regions/read", hotStatusHandler.GetHotReadRegions).Methods("GET")
	apiRouter.HandleFunc("/hotspot/stores", hotStatusHandler.GetHotStores).Methods("GET")

	regionHandler := newRegionHandler(svr, rd)
	clusterRouter.HandleFunc("/region/id/{id}", regionHandler.GetRegionByID).Methods("GET")
	clusterRouter.UseEncodedPath().HandleFunc("/region/key/{key}", regionHandler.GetRegionByKey).Methods("GET")

	srd := createStreamingRender()
	regionsAllHandler := newRegionsHandler(svr, srd)
	clusterRouter.HandleFunc("/regions", regionsAllHandler.GetAll).Methods("GET")

	regionsHandler := newRegionsHandler(svr, rd)
	clusterRouter.HandleFunc("/regions/key", regionsHandler.ScanRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/count", regionsHandler.GetRegionCount).Methods("GET")
	clusterRouter.HandleFunc("/regions/store/{id}", regionsHandler.GetStoreRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/writeflow", regionsHandler.GetTopWriteFlow).Methods("GET")
	clusterRouter.HandleFunc("/regions/readflow", regionsHandler.GetTopReadFlow).Methods("GET")
	clusterRouter.HandleFunc("/regions/confver", regionsHandler.GetTopConfVer).Methods("GET")
	clusterRouter.HandleFunc("/regions/version", regionsHandler.GetTopVersion).Methods("GET")
	clusterRouter.HandleFunc("/regions/size", regionsHandler.GetTopSize).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/miss-peer", regionsHandler.GetMissPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/extra-peer", regionsHandler.GetExtraPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/pending-peer", regionsHandler.GetPendingPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/down-peer", regionsHandler.GetDownPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/offline-peer", regionsHandler.GetOfflinePeer).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/empty-region", regionsHandler.GetEmptyRegion).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/hist-size", regionsHandler.GetSizeHistogram).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/hist-keys", regionsHandler.GetKeysHistogram).Methods("GET")
	clusterRouter.HandleFunc("/regions/sibling/{id}", regionsHandler.GetRegionSiblings).Methods("GET")
	clusterRouter.HandleFunc("/regions/accelerate-schedule", regionsHandler.AccelerateRegionsScheduleInRange).Methods("POST")

	apiRouter.Handle("/version", newVersionHandler(rd)).Methods("GET")
	apiRouter.Handle("/status", newStatusHandler(svr, rd)).Methods("GET")

	memberHandler := newMemberHandler(svr, rd)
	apiRouter.HandleFunc("/members", memberHandler.ListMembers).Methods("GET")
	apiRouter.HandleFunc("/members/name/{name}", memberHandler.DeleteByName).Methods("DELETE")
	apiRouter.HandleFunc("/members/id/{id}", memberHandler.DeleteByID).Methods("DELETE")
	apiRouter.HandleFunc("/members/name/{name}", memberHandler.SetMemberPropertyByName).Methods("POST")

	leaderHandler := newLeaderHandler(svr, rd)
	apiRouter.HandleFunc("/leader", leaderHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/leader/resign", leaderHandler.Resign).Methods("POST")
	apiRouter.HandleFunc("/leader/transfer/{next_leader}", leaderHandler.Transfer).Methods("POST")

	statsHandler := newStatsHandler(svr, rd)
	clusterRouter.HandleFunc("/stats/region", statsHandler.Region).Methods("GET")

	trendHandler := newTrendHandler(svr, rd)
	apiRouter.HandleFunc("/trend", trendHandler.Handle).Methods("GET")

	adminHandler := newAdminHandler(svr, rd)
	clusterRouter.HandleFunc("/admin/cache/region/{id}", adminHandler.HandleDropCacheRegion).Methods("DELETE")
	clusterRouter.HandleFunc("/admin/reset-ts", adminHandler.ResetTS).Methods("POST")
	apiRouter.HandleFunc("/admin/persist-file/{file_name}", adminHandler.persistFile).Methods("POST")
	clusterRouter.HandleFunc("/admin/replication_mode/wait-async", adminHandler.UpdateWaitAsyncTime).Methods("POST")

	logHandler := newLogHandler(svr, rd)
	apiRouter.HandleFunc("/admin/log", logHandler.Handle).Methods("POST")

	replicationModeHandler := newReplicationModeHandler(svr, rd)
	clusterRouter.HandleFunc("/replication_mode/status", replicationModeHandler.GetStatus)

	componentHandler := newComponentHandler(svr, rd)
	clusterRouter.HandleFunc("/component", componentHandler.Register).Methods("POST")
	clusterRouter.HandleFunc("/component/{component}/{addr}", componentHandler.UnRegister).Methods("DELETE")
	clusterRouter.HandleFunc("/component", componentHandler.GetAllAddress).Methods("GET")
	clusterRouter.HandleFunc("/component/{type}", componentHandler.GetAddress).Methods("GET")

	pluginHandler := newPluginHandler(handler, rd)
	apiRouter.HandleFunc("/plugin", pluginHandler.LoadPlugin).Methods("POST")
	apiRouter.HandleFunc("/plugin", pluginHandler.UnloadPlugin).Methods("DELETE")

	apiRouter.Handle("/health", newHealthHandler(svr, rd)).Methods("GET")
	apiRouter.Handle("/diagnose", newDiagnoseHandler(svr, rd)).Methods("GET")
	apiRouter.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")
	// metric query use to query metric data, the protocol is compatible with prometheus.
	apiRouter.Handle("/metric/query", newQueryMetric(svr)).Methods("GET", "POST")
	apiRouter.Handle("/metric/query_range", newQueryMetric(svr)).Methods("GET", "POST")

	// profile API
	apiRouter.HandleFunc("/debug/pprof/profile", pprof.Profile)
	apiRouter.Handle("/debug/pprof/heap", pprof.Handler("heap"))
	apiRouter.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
	apiRouter.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
	apiRouter.Handle("/debug/pprof/block", pprof.Handler("block"))
	apiRouter.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))

	// service GC safepoint API
	serviceGCSafepointHandler := newServiceGCSafepointHandler(svr, rd)
	apiRouter.HandleFunc("/gc/safepoint", serviceGCSafepointHandler.List).Methods("GET")
	apiRouter.HandleFunc("/gc/safepoint/{service_id}", serviceGCSafepointHandler.Delete).Methods("DELETE")

	// Deprecated
	rootRouter.Handle("/health", newHealthHandler(svr, rd)).Methods("GET")
	// Deprecated
	rootRouter.Handle("/diagnose", newDiagnoseHandler(svr, rd)).Methods("GET")
	// Deprecated
	rootRouter.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")

	return rootRouter
}
<commit_msg>misc: unify route path (#2969)<commit_after>// Copyright 2016 TiKV Project 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"context"
	"net/http"
	"net/http/pprof"

	"github.com/gorilla/mux"
	"github.com/tikv/pd/server"
	"github.com/unrolled/render"
)

func createStreamingRender() *render.Render {
	return render.New(render.Options{
		StreamingJSON: true,
	})
}

func createIndentRender() *render.Render {
	return render.New(render.Options{
		IndentJSON: true,
	})
}

// The returned function is used as a lazy router to avoid the data race problem.
// @title Placement Driver Core API
// @version 1.0
// @description This is placement driver.
// @contact.name Placement Driver Support
// @contact.url https://github.com/tikv/pd/issues
// @contact.email info@pingcap.com
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @BasePath /pd/api/v1
func createRouter(ctx context.Context, prefix string, svr *server.Server) *mux.Router {
	rd := createIndentRender()

	rootRouter := mux.NewRouter().PathPrefix(prefix).Subrouter()
	handler := svr.GetHandler()

	apiRouter := rootRouter.PathPrefix("/api/v1").Subrouter()

	clusterRouter := apiRouter.NewRoute().Subrouter()
	clusterRouter.Use(newClusterMiddleware(svr).Middleware)

	operatorHandler := newOperatorHandler(handler, rd)
	apiRouter.HandleFunc("/operators", operatorHandler.List).Methods("GET")
	apiRouter.HandleFunc("/operators", operatorHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Delete).Methods("DELETE")

	schedulerHandler := newSchedulerHandler(svr, rd)
	apiRouter.HandleFunc("/schedulers", schedulerHandler.List).Methods("GET")
	apiRouter.HandleFunc("/schedulers", schedulerHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.Delete).Methods("DELETE")
	apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.PauseOrResume).Methods("POST")

	schedulerConfigHandler := newSchedulerConfigHandler(svr, rd)
	apiRouter.PathPrefix("/scheduler-config").Handler(schedulerConfigHandler)

	clusterHandler := newClusterHandler(svr, rd)
	apiRouter.Handle("/cluster", clusterHandler).Methods("GET")
	apiRouter.HandleFunc("/cluster/status", clusterHandler.GetClusterStatus).Methods("GET")

	confHandler := newConfHandler(svr, rd)
	apiRouter.HandleFunc("/config", confHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/config", confHandler.Post).Methods("POST")
	apiRouter.HandleFunc("/config/default", confHandler.GetDefault).Methods("GET")
	apiRouter.HandleFunc("/config/schedule", confHandler.GetSchedule).Methods("GET")
	apiRouter.HandleFunc("/config/schedule", confHandler.SetSchedule).Methods("POST")
	apiRouter.HandleFunc("/config/replicate", confHandler.GetReplication).Methods("GET")
	apiRouter.HandleFunc("/config/replicate", confHandler.SetReplication).Methods("POST")
	apiRouter.HandleFunc("/config/label-property", confHandler.GetLabelProperty).Methods("GET")
	apiRouter.HandleFunc("/config/label-property", confHandler.SetLabelProperty).Methods("POST")
	apiRouter.HandleFunc("/config/cluster-version", confHandler.GetClusterVersion).Methods("GET")
	apiRouter.HandleFunc("/config/cluster-version", confHandler.SetClusterVersion).Methods("POST")
	apiRouter.HandleFunc("/config/replication-mode", confHandler.GetReplicationMode).Methods("GET")
	apiRouter.HandleFunc("/config/replication-mode", confHandler.SetReplicationMode).Methods("POST")

	rulesHandler := newRulesHandler(svr, rd)
	clusterRouter.HandleFunc("/config/rules", rulesHandler.GetAll).Methods("GET")
	clusterRouter.HandleFunc("/config/rules", rulesHandler.SetAll).Methods("POST")
	clusterRouter.HandleFunc("/config/rules/batch", rulesHandler.Batch).Methods("POST")
	clusterRouter.HandleFunc("/config/rules/group/{group}", rulesHandler.GetAllByGroup).Methods("GET")
	clusterRouter.HandleFunc("/config/rules/region/{region}", rulesHandler.GetAllByRegion).Methods("GET")
	clusterRouter.HandleFunc("/config/rules/key/{key}", rulesHandler.GetAllByKey).Methods("GET")
	clusterRouter.HandleFunc("/config/rule/{group}/{id}", rulesHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/config/rule", rulesHandler.Set).Methods("POST")
	clusterRouter.HandleFunc("/config/rule/{group}/{id}", rulesHandler.Delete).Methods("DELETE")

	clusterRouter.HandleFunc("/config/rule_group/{id}", rulesHandler.GetGroupConfig).Methods("GET")
	clusterRouter.HandleFunc("/config/rule_group", rulesHandler.SetGroupConfig).Methods("POST")
	clusterRouter.HandleFunc("/config/rule_group/{id}", rulesHandler.DeleteGroupConfig).Methods("DELETE")
	clusterRouter.HandleFunc("/config/rule_groups", rulesHandler.GetAllGroupConfigs).Methods("GET")

	clusterRouter.HandleFunc("/config/placement-rule", rulesHandler.GetAllGroupBundles).Methods("GET")
	clusterRouter.HandleFunc("/config/placement-rule", rulesHandler.SetAllGroupBundles).Methods("POST")
	// {group} can be a regular expression, we should enable path encode to
	// support special characters.
	escapeRouter := clusterRouter.NewRoute().Subrouter().UseEncodedPath()
	clusterRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.GetGroupBundle).Methods("GET")
	clusterRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.SetGroupBundle).Methods("POST")
	escapeRouter.HandleFunc("/config/placement-rule/{group}", rulesHandler.DeleteGroupBundle).Methods("DELETE")

	storeHandler := newStoreHandler(handler, rd)
	clusterRouter.HandleFunc("/store/{id}", storeHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/store/{id}", storeHandler.Delete).Methods("DELETE")
	clusterRouter.HandleFunc("/store/{id}/state", storeHandler.SetState).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/label", storeHandler.SetLabels).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/weight", storeHandler.SetWeight).Methods("POST")
	clusterRouter.HandleFunc("/store/{id}/limit", storeHandler.SetLimit).Methods("POST")
	storesHandler := newStoresHandler(handler, rd)
	clusterRouter.Handle("/stores", storesHandler).Methods("GET")
	clusterRouter.HandleFunc("/stores/remove-tombstone", storesHandler.RemoveTombStone).Methods("DELETE")
	clusterRouter.HandleFunc("/stores/limit", storesHandler.GetAllLimit).Methods("GET")
	clusterRouter.HandleFunc("/stores/limit", storesHandler.SetAllLimit).Methods("POST")
	clusterRouter.HandleFunc("/stores/limit/scene", storesHandler.SetStoreLimitScene).Methods("POST")
	clusterRouter.HandleFunc("/stores/limit/scene", storesHandler.GetStoreLimitScene).Methods("GET")

	labelsHandler := newLabelsHandler(svr, rd)
	clusterRouter.HandleFunc("/labels", labelsHandler.Get).Methods("GET")
	clusterRouter.HandleFunc("/labels/stores", labelsHandler.GetStores).Methods("GET")

	hotStatusHandler := newHotStatusHandler(handler, rd)
	apiRouter.HandleFunc("/hotspot/regions/write", hotStatusHandler.GetHotWriteRegions).Methods("GET")
	apiRouter.HandleFunc("/hotspot/regions/read", hotStatusHandler.GetHotReadRegions).Methods("GET")
	apiRouter.HandleFunc("/hotspot/stores", hotStatusHandler.GetHotStores).Methods("GET")

	regionHandler := newRegionHandler(svr, rd)
	clusterRouter.HandleFunc("/region/id/{id}", regionHandler.GetRegionByID).Methods("GET")
	clusterRouter.UseEncodedPath().HandleFunc("/region/key/{key}", regionHandler.GetRegionByKey).Methods("GET")

	srd := createStreamingRender()
	regionsAllHandler := newRegionsHandler(svr, srd)
	clusterRouter.HandleFunc("/regions", regionsAllHandler.GetAll).Methods("GET")

	regionsHandler := newRegionsHandler(svr, rd)
	clusterRouter.HandleFunc("/regions/key", regionsHandler.ScanRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/count", regionsHandler.GetRegionCount).Methods("GET")
	clusterRouter.HandleFunc("/regions/store/{id}", regionsHandler.GetStoreRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/writeflow", regionsHandler.GetTopWriteFlow).Methods("GET")
	clusterRouter.HandleFunc("/regions/readflow", regionsHandler.GetTopReadFlow).Methods("GET")
	clusterRouter.HandleFunc("/regions/confver", regionsHandler.GetTopConfVer).Methods("GET")
	clusterRouter.HandleFunc("/regions/version", regionsHandler.GetTopVersion).Methods("GET")
	clusterRouter.HandleFunc("/regions/size", regionsHandler.GetTopSize).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/miss-peer", regionsHandler.GetMissPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/extra-peer", regionsHandler.GetExtraPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/pending-peer", regionsHandler.GetPendingPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/down-peer", regionsHandler.GetDownPeerRegions).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/offline-peer", regionsHandler.GetOfflinePeer).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/empty-region", regionsHandler.GetEmptyRegion).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/hist-size", regionsHandler.GetSizeHistogram).Methods("GET")
	clusterRouter.HandleFunc("/regions/check/hist-keys", regionsHandler.GetKeysHistogram).Methods("GET")
	clusterRouter.HandleFunc("/regions/sibling/{id}", regionsHandler.GetRegionSiblings).Methods("GET")
	clusterRouter.HandleFunc("/regions/accelerate-schedule", regionsHandler.AccelerateRegionsScheduleInRange).Methods("POST")

	apiRouter.Handle("/version", newVersionHandler(rd)).Methods("GET")
	apiRouter.Handle("/status", newStatusHandler(svr, rd)).Methods("GET")

	memberHandler := newMemberHandler(svr, rd)
	apiRouter.HandleFunc("/members", memberHandler.ListMembers).Methods("GET")
	apiRouter.HandleFunc("/members/name/{name}", memberHandler.DeleteByName).Methods("DELETE")
	apiRouter.HandleFunc("/members/id/{id}", memberHandler.DeleteByID).Methods("DELETE")
	apiRouter.HandleFunc("/members/name/{name}", memberHandler.SetMemberPropertyByName).Methods("POST")

	leaderHandler := newLeaderHandler(svr, rd)
	apiRouter.HandleFunc("/leader", leaderHandler.Get).Methods("GET")
	apiRouter.HandleFunc("/leader/resign", leaderHandler.Resign).Methods("POST")
	apiRouter.HandleFunc("/leader/transfer/{next_leader}", leaderHandler.Transfer).Methods("POST")

	statsHandler := newStatsHandler(svr, rd)
	clusterRouter.HandleFunc("/stats/region", statsHandler.Region).Methods("GET")

	trendHandler := newTrendHandler(svr, rd)
	apiRouter.HandleFunc("/trend", trendHandler.Handle).Methods("GET")

	adminHandler := newAdminHandler(svr, rd)
	clusterRouter.HandleFunc("/admin/cache/region/{id}", adminHandler.HandleDropCacheRegion).Methods("DELETE")
	clusterRouter.HandleFunc("/admin/reset-ts", adminHandler.ResetTS).Methods("POST")
	apiRouter.HandleFunc("/admin/persist-file/{file_name}", adminHandler.persistFile).Methods("POST")
	clusterRouter.HandleFunc("/admin/replication_mode/wait-async", adminHandler.UpdateWaitAsyncTime).Methods("POST")

	logHandler := newLogHandler(svr, rd)
	apiRouter.HandleFunc("/admin/log", logHandler.Handle).Methods("POST")

	replicationModeHandler := newReplicationModeHandler(svr, rd)
	clusterRouter.HandleFunc("/replication_mode/status", replicationModeHandler.GetStatus)

	componentHandler := newComponentHandler(svr, rd)
	clusterRouter.HandleFunc("/component", componentHandler.Register).Methods("POST")
	clusterRouter.HandleFunc("/component/{component}/{addr}", componentHandler.UnRegister).Methods("DELETE")
	clusterRouter.HandleFunc("/component", componentHandler.GetAllAddress).Methods("GET")
	clusterRouter.HandleFunc("/component/{type}", componentHandler.GetAddress).Methods("GET")

	pluginHandler := newPluginHandler(handler, rd)
	apiRouter.HandleFunc("/plugin", pluginHandler.LoadPlugin).Methods("POST")
	apiRouter.HandleFunc("/plugin", pluginHandler.UnloadPlugin).Methods("DELETE")

	apiRouter.Handle("/health", newHealthHandler(svr, rd)).Methods("GET")
	apiRouter.Handle("/diagnose", newDiagnoseHandler(svr, rd)).Methods("GET")
	apiRouter.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")
	// metric query use to query metric data, the protocol is compatible with prometheus.
	apiRouter.Handle("/metric/query", newQueryMetric(svr)).Methods("GET", "POST")
	apiRouter.Handle("/metric/query_range", newQueryMetric(svr)).Methods("GET", "POST")

	// profile API
	apiRouter.HandleFunc("/debug/pprof/profile", pprof.Profile)
	apiRouter.Handle("/debug/pprof/heap", pprof.Handler("heap"))
	apiRouter.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
	apiRouter.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
	apiRouter.Handle("/debug/pprof/block", pprof.Handler("block"))
	apiRouter.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))

	// service GC safepoint API
	serviceGCSafepointHandler := newServiceGCSafepointHandler(svr, rd)
	apiRouter.HandleFunc("/gc/safepoint", serviceGCSafepointHandler.List).Methods("GET")
	apiRouter.HandleFunc("/gc/safepoint/{service_id}", serviceGCSafepointHandler.Delete).Methods("DELETE")

	// Deprecated
	rootRouter.Handle("/health", newHealthHandler(svr, rd)).Methods("GET")
	// Deprecated
	rootRouter.Handle("/diagnose", newDiagnoseHandler(svr, rd)).Methods("GET")
	// Deprecated
	rootRouter.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")

	return rootRouter
}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope 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 triggers

import (
	"time"

	"github.com/zagrodzki/goscope/scope"
)

// autoDelay controls the delay for triggering without condition in ModeAuto.
// Used in tests.
var autoDelay = 500 * scope.Millisecond

// Trigger represents a filter running on the data channel, waiting for
// a triggering event and then allowing a set of samples equal to the
// configured timebase.
type Trigger struct {
	source   scope.ChanID
	slope    *RisingEdge
	lvl      *Level
	rec      scope.DataRecorder
	interval scope.Duration
	tbCount  int
	mode     *Mode
}

// New returns an initialized Trigger.
func New(rec scope.DataRecorder) *Trigger {
	return &Trigger{
		rec:   rec,
		mode:  newModeParam(),
		slope: newEdgeParam(),
		lvl:   newLevelParam(),
	}
}

// TimeBase returns the trigger timebase, which is the same as the underlying recorder timebase.
func (t *Trigger) TimeBase() scope.Duration {
	return t.rec.TimeBase()
}

// Reset initializes the recording.
func (t *Trigger) Reset(i scope.Duration, ch <-chan []scope.ChannelData) {
	out := make(chan []scope.ChannelData, 20)
	t.interval = i
	t.tbCount = int(t.rec.TimeBase() / i)
	t.rec.Reset(i, out)
	go t.run(ch, out)
}

// Error passes the error down to the underlying recorder.
func (t *Trigger) Error(err error) {
	t.rec.Error(err)
}

// Source sets the source for the trigger. If received data doesn't contain
// samples for specified source, the trigger allows all samples without filtering.
func (t *Trigger) Source(id scope.ChanID) {
	t.source = id
}

// TriggerParams returns the trigger params.
func (t *Trigger) TriggerParams() []scope.Param {
	return []scope.Param{
		t.slope,
		t.mode,
		t.lvl,
	}
}

type thresholdState int

const (
	belowThreshold        thresholdState = -1
	unknownThresholdState thresholdState = 0
	aboveThreshold        thresholdState = 1
)

func edgeType(prevState, newState thresholdState) RisingEdge {
	switch {
	case prevState == newState:
		return EdgeNone
	case prevState < newState:
		return EdgeRising
	case prevState > newState:
		return EdgeFalling
	}
	return EdgeNone
}

type slice struct {
	begin int
	end   int
}

func (t *Trigger) run(in <-chan []scope.ChannelData, out chan<- []scope.ChannelData) {
	var left, source, ignored int
	var trg, scanned, found bool
	var newState, prevState thresholdState
	var lastTrg time.Time
	maxIgnored := int(autoDelay / t.interval)
	slope := *t.slope
	mode := *t.mode
	lvl := t.lvl.v
	for d := range in {
		if !scanned {
			scanned = true
			for i := range d {
				if d[i].ID == t.source {
					source = i
					found = true
					break
				}
			}
		}
		if !found {
			out <- d
			continue
		}
		num := len(d[source].Samples)
		// slices keeps indices of the samples that should be pushed out
		var outSlices []slice
		var curSlice slice
		for i, v := range d[source].Samples {
			switch {
			case v > lvl:
				newState = aboveThreshold
			case v < lvl:
				newState = belowThreshold
			}
			// if the previous state was uninitialized, do not trigger.
			// Once state is initialized, it's always either above or below, never unknown.
			if newState != prevState && prevState == unknownThresholdState {
				prevState = newState
			}
			// newState > prevState means we moved from below threshold to above threshold, i.e. rising slope.
			if !trg {
				switch {
				// mode single and triggered once already. Don't trigger.
				case mode == ModeSingle && !lastTrg.IsZero():
				// crossed the threshold
				case edgeType(prevState, newState) == slope:
					trg = true
				// mode auto and time elapsed since last trigger.
				case mode == ModeAuto && ignored >= maxIgnored:
					trg = true
				}
				if trg {
					lastTrg = time.Now()
					left = t.tbCount
					curSlice.begin = i
					ignored = 0
				}
			}
			if trg {
				curSlice.end = i + 1
				left--
				if left == 0 {
					outSlices = append(outSlices, curSlice)
					curSlice = slice{}
					trg = false
				}
			} else {
				ignored++
			}
			prevState = newState
			num--
		}
		if trg {
			outSlices = append(outSlices, curSlice)
		}
		// flush samples
		if len(outSlices) > 0 {
			for _, b := range outSlices {
				chunk := make([]scope.ChannelData, len(d))
				for ch := range d {
					chunk[ch].ID = d[ch].ID
					chunk[ch].Samples = d[ch].Samples[b.begin:b.end]
				}
				out <- chunk
			}
			outSlices = outSlices[:0]
		}
	}
	close(out)
}
<commit_msg>Make the buffer smaller, to reduce the number of output frames we need to wait until fresh input is visible. I'll have to rethink how much input we capture in one go...<commit_after>//  Copyright 2016 The goscope 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 triggers

import (
	"time"

	"github.com/zagrodzki/goscope/scope"
)

// autoDelay controls the delay for triggering without condition in ModeAuto.
// Used in tests.
var autoDelay = 500 * scope.Millisecond

// Trigger represents a filter running on the data channel, waiting for
// a triggering event and then allowing a set of samples equal to the
// configured timebase.
type Trigger struct {
	source   scope.ChanID
	slope    *RisingEdge
	lvl      *Level
	rec      scope.DataRecorder
	interval scope.Duration
	tbCount  int
	mode     *Mode
}

// New returns an initialized Trigger.
func New(rec scope.DataRecorder) *Trigger {
	return &Trigger{
		rec:   rec,
		mode:  newModeParam(),
		slope: newEdgeParam(),
		lvl:   newLevelParam(),
	}
}

// TimeBase returns the trigger timebase, which is the same as the underlying recorder timebase.
func (t *Trigger) TimeBase() scope.Duration {
	return t.rec.TimeBase()
}

// Reset initializes the recording.
func (t *Trigger) Reset(i scope.Duration, ch <-chan []scope.ChannelData) {
	out := make(chan []scope.ChannelData, 2)
	t.interval = i
	t.tbCount = int(t.rec.TimeBase() / i)
	t.rec.Reset(i, out)
	go t.run(ch, out)
}

// Error passes the error down to the underlying recorder.
func (t *Trigger) Error(err error) {
	t.rec.Error(err)
}

// Source sets the source for the trigger. If received data doesn't contain
// samples for specified source, the trigger allows all samples without filtering.
func (t *Trigger) Source(id scope.ChanID) {
	t.source = id
}

// TriggerParams returns the trigger params.
func (t *Trigger) TriggerParams() []scope.Param {
	return []scope.Param{
		t.slope,
		t.mode,
		t.lvl,
	}
}

type thresholdState int

const (
	belowThreshold        thresholdState = -1
	unknownThresholdState thresholdState = 0
	aboveThreshold        thresholdState = 1
)

func edgeType(prevState, newState thresholdState) RisingEdge {
	switch {
	case prevState == newState:
		return EdgeNone
	case prevState < newState:
		return EdgeRising
	case prevState > newState:
		return EdgeFalling
	}
	return EdgeNone
}

type slice struct {
	begin int
	end   int
}

func (t *Trigger) run(in <-chan []scope.ChannelData, out chan<- []scope.ChannelData) {
	var left, source, ignored int
	var trg, scanned, found bool
	var newState, prevState thresholdState
	var lastTrg time.Time
	maxIgnored := int(autoDelay / t.interval)
	slope := *t.slope
	mode := *t.mode
	lvl := t.lvl.v
	for d := range in {
		if !scanned {
			scanned = true
			for i := range d {
				if d[i].ID == t.source {
					source = i
					found = true
					break
				}
			}
		}
		if !found {
			out <- d
			continue
		}
		num := len(d[source].Samples)
		// slices keeps indices of the samples that should be pushed out
		var outSlices []slice
		var curSlice slice
		for i, v := range d[source].Samples {
			switch {
			case v > lvl:
				newState = aboveThreshold
			case v < lvl:
				newState = belowThreshold
			}
			// if the previous state was uninitialized, do not trigger.
			// Once state is initialized, it's always either above or below, never unknown.
			if newState != prevState && prevState == unknownThresholdState {
				prevState = newState
			}
			// newState > prevState means we moved from below threshold to above threshold, i.e. rising slope.
			if !trg {
				switch {
				// mode single and triggered once already. Don't trigger.
				case mode == ModeSingle && !lastTrg.IsZero():
				// crossed the threshold
				case edgeType(prevState, newState) == slope:
					trg = true
				// mode auto and time elapsed since last trigger.
				case mode == ModeAuto && ignored >= maxIgnored:
					trg = true
				}
				if trg {
					lastTrg = time.Now()
					left = t.tbCount
					curSlice.begin = i
					ignored = 0
				}
			}
			if trg {
				curSlice.end = i + 1
				left--
				if left == 0 {
					outSlices = append(outSlices, curSlice)
					curSlice = slice{}
					trg = false
				}
			} else {
				ignored++
			}
			prevState = newState
			num--
		}
		if trg {
			outSlices = append(outSlices, curSlice)
		}
		// flush samples
		if len(outSlices) > 0 {
			for _, b := range outSlices {
				chunk := make([]scope.ChannelData, len(d))
				for ch := range d {
					chunk[ch].ID = d[ch].ID
					chunk[ch].Samples = d[ch].Samples[b.begin:b.end]
				}
				out <- chunk
			}
			outSlices = outSlices[:0]
		}
	}
	close(out)
}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope 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 triggers

import (
	"github.com/zagrodzki/goscope/scope"
)

// RisingEdge represents the trigger edge type, rising or falling
type RisingEdge bool

// RisingEdge values for readability.
const (
	Rising  RisingEdge = true
	Falling RisingEdge = false
)

// Trigger represents a filter running on the data channel, waiting for
// a triggering event and then allowing a set of samples equal to the
// configured timebase.
type Trigger struct {
	source  scope.ChanID
	slope   RisingEdge
	lvl     scope.Voltage
	rec     scope.DataRecorder
	tbCount int
}

// New returns an initialized Trigger.
func New(rec scope.DataRecorder) *Trigger {
	return &Trigger{
		rec: rec,
	}
}

// TimeBase returns the trigger timebase, which is the same as the underlying recorder timebase.
func (t *Trigger) TimeBase() scope.Duration {
	return t.rec.TimeBase()
}

// Reset initializes the recording.
func (t *Trigger) Reset(i scope.Duration, ch <-chan []scope.ChannelData) {
	out := make(chan []scope.ChannelData, 20)
	t.tbCount = int(t.rec.TimeBase() / i)
	t.rec.Reset(i, out)
	go t.run(ch, out)
}

// Error passes the error down to the underlying recorder.
func (t *Trigger) Error(err error) {
	t.rec.Error(err)
}

// Source sets the source for the trigger. If received data doesn't contain
// samples for specified source, the trigger allows all samples without filtering.
func (t *Trigger) Source(id scope.ChanID) {
	t.source = id
}

// Edge configures the type of edge (rising/falling) that is the triggering condition.
func (t *Trigger) Edge(e RisingEdge) {
	t.slope = e
}

// Level configures the level that the edge has to cross for the triggering condition.
func (t *Trigger) Level(l scope.Voltage) {
	t.lvl = l
}

func (t *Trigger) run(in <-chan []scope.ChannelData, out chan<- []scope.ChannelData) {
	var left int
	var last scope.Voltage
	var source int
	var trg, scanned, found, initialized bool
	for d := range in {
		if !scanned {
			scanned = true
			for i := range d {
				if d[i].ID == t.source {
					source = i
					found = true
					break
				}
			}
		}
		if !found {
			out <- d
			continue
		}
		num := len(d[source].Samples)
		if !initialized && num > 0 {
			initialized = true
			last = d[source].Samples[0]
		}
		for num > 0 {
			// look for trigger
			if !trg {
				for i, v := range d[source].Samples {
					switch t.slope {
					case Rising:
						trg = last < t.lvl && v > t.lvl
					case Falling:
						trg = last > t.lvl && v < t.lvl
					}
					if trg {
						left = t.tbCount
						for ch := range d {
							d[ch].Samples = d[ch].Samples[i:]
						}
						break
					}
					last = v
					num--
				}
			}
			// flush samples
			if trg {
				if left >= num {
					left -= num
					num = 0
					out <- d
				} else {
					// we need to send only a small chunk
					chunk := make([]scope.ChannelData, len(d))
					for ch := range d {
						chunk[ch].ID = d[ch].ID
						chunk[ch].Samples = d[ch].Samples[:left]
					}
					last = d[source].Samples[left-1]
					num -= left
					left = 0
					out <- chunk
				}
				if left == 0 {
					trg = false
				}
			}
		}
	}
	close(out)
}
<commit_msg>Change the triggering mechanism, use one of two states (above/below), trigger on every sample, when triggering put an index pair (slices) to the output list, then flush all index pairs.<commit_after>//  Copyright 2016 The goscope 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 triggers

import (
	"github.com/zagrodzki/goscope/scope"
)

// RisingEdge represents the trigger edge type, rising or falling
type RisingEdge bool

// RisingEdge values for readability.
const (
	Rising  RisingEdge = true
	Falling RisingEdge = false
)

// Trigger represents a filter running on the data channel, waiting for
// a triggering event and then allowing a set of samples equal to the
// configured timebase.
type Trigger struct {
	source  scope.ChanID
	slope   RisingEdge
	lvl     scope.Voltage
	rec     scope.DataRecorder
	tbCount int
}

// New returns an initialized Trigger.
func New(rec scope.DataRecorder) *Trigger {
	return &Trigger{
		rec: rec,
	}
}

// TimeBase returns the trigger timebase, which is the same as the underlying recorder timebase.
func (t *Trigger) TimeBase() scope.Duration {
	return t.rec.TimeBase()
}

// Reset initializes the recording.
func (t *Trigger) Reset(i scope.Duration, ch <-chan []scope.ChannelData) {
	out := make(chan []scope.ChannelData, 20)
	t.tbCount = int(t.rec.TimeBase() / i)
	t.rec.Reset(i, out)
	go t.run(ch, out)
}

// Error passes the error down to the underlying recorder.
func (t *Trigger) Error(err error) {
	t.rec.Error(err)
}

// Source sets the source for the trigger. If received data doesn't contain
// samples for specified source, the trigger allows all samples without filtering.
func (t *Trigger) Source(id scope.ChanID) {
	t.source = id
}

// Edge configures the type of edge (rising/falling) that is the triggering condition.
func (t *Trigger) Edge(e RisingEdge) {
	t.slope = e
}

// Level configures the level that the edge has to cross for the triggering condition.
func (t *Trigger) Level(l scope.Voltage) {
	t.lvl = l
}

type thresholdState int

const (
	belowThreshold        thresholdState = -1
	unknownThresholdState thresholdState = 0
	aboveThreshold        thresholdState = 1
)

type slice struct {
	begin int
	end   int
}

func (t *Trigger) run(in <-chan []scope.ChannelData, out chan<- []scope.ChannelData) {
	var left, source int
	var trg, scanned, found bool
	var newState, prevState thresholdState
	for d := range in {
		if !scanned {
			scanned = true
			for i := range d {
				if d[i].ID == t.source {
					source = i
					found = true
					break
				}
			}
		}
		if !found {
			out <- d
			continue
		}
		num := len(d[source].Samples)
		// slices keeps indices of the samples that should be pushed out
		var outSlices []slice
		var curSlice slice
		for i, v := range d[source].Samples {
			switch {
			case v > t.lvl:
				newState = aboveThreshold
			case v < t.lvl:
				newState = belowThreshold
			}
			// if the previous state was uninitialized, do not trigger.
			// Once state is initialized, it's always either above or below, never unknown.
			if newState != prevState && prevState == unknownThresholdState {
				prevState = newState
			}
			// newState > prevState means we moved from below threshold to above threshold, i.e. rising slope.
			if !trg && newState != prevState && RisingEdge(newState > prevState) == t.slope {
				trg = true
				left = t.tbCount
				curSlice.begin = i
			}
			if trg {
				curSlice.end = i + 1
				left--
				if left == 0 {
					outSlices = append(outSlices, curSlice)
					curSlice = slice{}
					trg = false
				}
			}
			prevState = newState
			num--
		}
		if trg {
			outSlices = append(outSlices, curSlice)
			curSlice = slice{}
		}
		// flush samples
		if len(outSlices) > 0 {
			chunk := make([]scope.ChannelData, len(d))
			for ch := range d {
				chunk[ch].ID = d[ch].ID
			}
			for _, b := range outSlices {
				for ch := range d {
					chunk[ch].Samples = d[ch].Samples[b.begin:b.end]
				}
				out <- chunk
			}
			outSlices = outSlices[:0]
		}
	}
	close(out)
}
<|endoftext|>


<commit_before>/*
The memberlist package is used to provide a lightweight gossip based
mechanism for node membership and failure detection. It is loosely
based on the SWIM paper (Scalable Weakly-consistent Infection-style
process group Membership protocol). There are a few notable differences,
including the uses of additional gossip (instead of purely piggybacking on
failure detection) and the addition of a state push/pull mechanism.

An independent gossip mechanism is used because it allows for changes to be propogated
more quickly, and also enables us to gossip at a different interval that we perform
failure checks. The gossip rate is tunable, and can be disabled.

A Push/Pull mechanism is also included because it allows new nodes to
get an almost complete member list upon joining. It also is used as
a periodic anti-entropy mechanism to ensure very high convergence rates.
The frequency of this can be adjusted to change the overhead, or disabled
entirely.

*/
package memberlist

import (
	"fmt"
	"net"
	"os"
	"sync"
	"time"
)

type Delegate interface {
	// NodeMeta is used to retrieve meta-data about the current node
	// when broadcasting an alive message. It's length is limited to
	// the given byte size.
	NodeMeta(limit int) []byte

	// NotifyMsg is called when a user-data message is received.
	// This should not block
	NotifyMsg([]byte)

	// GetBroadcasts is called when user data messages can be broadcast.
	// It can return a list of buffers to send. Each buffer should assume an
	// overhead as provided with a limit on the total byte size allowed.
	GetBroadcasts(overhead, limit int) [][]byte

	// LocalState is used for a TCP Push/Pull. This is sent to
	// the remote side as well as membership information
	LocalState() []byte

	// MergeRemoteState is invoked after a TCP Push/Pull. This is the
	// state received from the remote side.
	MergeRemoteState([]byte)
}

type Config struct {
	Name             string        // Node name (FQDN)
	BindAddr         string        // Binding address
	UDPPort          int           // UDP port to listen on
	TCPPort          int           // TCP port to listen on
	TCPTimeout       time.Duration // TCP timeout
	IndirectChecks   int           // Number of indirect checks to use
	RetransmitMult   int           // Retransmits = RetransmitMult * log(N+1)
	SuspicionMult    int           // Suspicion time = SuspcicionMult * log(N+1) * Interval
	PushPullInterval time.Duration // How often we do a Push/Pull update
	RTT              time.Duration // 99% precentile of round-trip-time
	ProbeInterval    time.Duration // Failure probing interval length

	GossipNodes    int           // Number of nodes to gossip to per GossipInterval
	GossipInterval time.Duration // Gossip interval for non-piggyback messages (only if GossipNodes > 0)

	JoinCh       chan<- *Node
	LeaveCh      chan<- *Node
	UserDelegate Delegate // Delegate for user data
}

type Memberlist struct {
	config         *Config
	shutdown       bool
	leave          bool
	leaveBroadcast chan struct{}

	udpListener *net.UDPConn
	tcpListener *net.TCPListener

	sequenceNum uint32 // Local sequence number
	incarnation uint32 // Local incarnation number

	nodeLock sync.RWMutex
	nodes    []*nodeState          // Known nodes
	nodeMap  map[string]*nodeState // Maps Addr.String() -> NodeState

	tickerLock sync.Mutex
	tickers    []*time.Ticker
	stopTick   chan struct{}
	probeIndex int

	ackLock     sync.Mutex
	ackHandlers map[uint32]*ackHandler

	broadcasts *TransmitLimitedQueue
}

// DefaultConfig is used to return a default sane set of configurations for
// Memberlist. It uses the hostname as the node name, and otherwise sets conservative
// values that are sane for most LAN environments.
func DefaultConfig() *Config {
	hostname, _ := os.Hostname()
	return &Config{
		hostname,
		"0.0.0.0",
		7946,
		7946,
		10 * time.Second,      // Timeout after 10 seconds
		3,                     // Use 3 nodes for the indirect ping
		4,                     // Retransmit a message 4 * log(N+1) nodes
		5,                     // Suspect a node for 5 * log(N+1) * Interval
		30 * time.Second,      // Low frequency
		20 * time.Millisecond, // Reasonable RTT time for LAN
		1 * time.Second,       // Failure check every second

		3, // Gossip to 3 nodes
		200 * time.Millisecond, // Gossip more rapidly

		nil,
		nil,
		nil,
	}
}

// newMemberlist creates the network listeners.
// Does not schedule exeuction of background maintenence.
func newMemberlist(conf *Config) (*Memberlist, error) {
	tcpAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.TCPPort)
	tcpLn, err := net.Listen("tcp", tcpAddr)
	if err != nil {
		return nil, fmt.Errorf("Failed to start TCP listener. Err: %s", err)
	}

	udpAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.UDPPort)
	udpLn, err := net.ListenPacket("udp", udpAddr)
	if err != nil {
		tcpLn.Close()
		return nil, fmt.Errorf("Failed to start UDP listener. Err: %s", err)
	}

	// Set the UDP receive window size
	setUDPRecvBuf(udpLn.(*net.UDPConn))

	m := &Memberlist{config: conf,
		leaveBroadcast: make(chan struct{}, 1),
		udpListener:    udpLn.(*net.UDPConn),
		tcpListener:    tcpLn.(*net.TCPListener),
		nodeMap:        make(map[string]*nodeState),
		stopTick:       make(chan struct{}, 32),
		ackHandlers:    make(map[uint32]*ackHandler),
		broadcasts:     &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult},
	}
	m.broadcasts.NumNodes = func() int { return len(m.nodes) }
	go m.tcpListen()
	go m.udpListen()
	return m, nil
}

// Create will start memberlist but does not connect to any other node
func Create(conf *Config) (*Memberlist, error) {
	m, err := newMemberlist(conf)
	if err != nil {
		return nil, err
	}
	if err := m.setAlive(); err != nil {
		m.Shutdown()
		return nil, err
	}
	m.schedule()
	return m, nil
}

// Join is used to take an existing Memberlist and attempt to join
// a cluster by contacting all the given hosts. Returns the number successfully,
// contacted and an error if none could be reached.
func (m *Memberlist) Join(existing []string) (int, error) {
	// Attempt to join any of them
	numSuccess := 0
	var retErr error
	for _, exist := range existing {
		addr, err := net.ResolveIPAddr("ip", exist)
		if err != nil {
			retErr = err
			continue
		}

		if err := m.pushPullNode(addr.IP); err != nil {
			retErr = err
			continue
		}

		numSuccess++
	}

	if numSuccess > 0 {
		retErr = nil
	}
	return numSuccess, retErr
}

// setAlive is used to mark this node as being alive
func (m *Memberlist) setAlive() error {
	// Pick a private IP address
	var ipAddr []byte
	if m.config.BindAddr == "0.0.0.0" {
		// Get the interfaces
		addresses, err := net.InterfaceAddrs()
		if err != nil {
			return fmt.Errorf("Failed to get interface addresses! Err: %vn", err)
		}

		// Find private IPv4 address
		for _, addr := range addresses {
			ip, ok := addr.(*net.IPNet)
			if !ok {
				continue
			}
			if ip.IP.To4() == nil {
				continue
			}
			if !isPrivateIP(ip.IP.String()) {
				continue
			}
			ipAddr = ip.IP
			break
		}

		// Failed to find private IP, use loopback
		if ipAddr == nil {
			ipAddr = []byte{127, 0, 0, 1}
		}

	} else {
		addr := m.tcpListener.Addr().(*net.TCPAddr)
		ipAddr = addr.IP
	}

	// Get the node meta data
	var meta []byte
	if m.config.UserDelegate != nil {
		meta = m.config.UserDelegate.NodeMeta(metaMaxSize)
		if len(meta) > metaMaxSize {
			panic("Node meta data provided is longer than the limit")
		}
	}

	a := alive{
		Incarnation: m.nextIncarnation(),
		Node:        m.config.Name,
		Addr:        ipAddr,
		Meta:        meta,
	}
	m.aliveNode(&a)
	return nil
}

// Members is used to return a list of all known live nodes
func (m *Memberlist) Members() []*Node {
	nodes := make([]*Node, 0, len(m.nodes))
	m.nodeLock.RLock()
	defer m.nodeLock.RUnlock()
	for _, n := range m.nodes {
		if n.State != stateDead {
			nodes = append(nodes, &n.Node)
		}
	}
	return nodes
}

// NumMembers provides an efficient way to determine
// the number of alive members
func (m *Memberlist) NumMembers() (alive int) {
	m.nodeLock.RLock()
	defer m.nodeLock.RUnlock()
	for _, n := range m.nodes {
		if n.State != stateDead {
			alive++
		}
	}
	return
}

// Leave will broadcast a leave message but will not shutdown
// the memberlist background maintenence. This should be followed
// by a Shutdown(). This will block until the death message has
// finished gossiping out.
func (m *Memberlist) Leave() error {
	m.leave = true
	d := dead{Incarnation: m.incarnation, Node: m.config.Name}
	m.deadNode(&d)

	// Block until the broadcast goes out
	if len(m.nodes) > 1 {
		<-m.leaveBroadcast
	}
	return nil
}

// Shutdown will stop the memberlist background maintenence
// but will not broadcast a leave message prior. If no prior
// leave was issued, other nodes will detect this as a failure.
func (m *Memberlist) Shutdown() error {
	m.shutdown = true
	m.deschedule()
	m.udpListener.Close()
	m.tcpListener.Close()
	return nil
}
<commit_msg>Good form to use keys with big structs<commit_after>/*
The memberlist package is used to provide a lightweight gossip based
mechanism for node membership and failure detection. It is loosely
based on the SWIM paper (Scalable Weakly-consistent Infection-style
process group Membership protocol). There are a few notable differences,
including the uses of additional gossip (instead of purely piggybacking on
failure detection) and the addition of a state push/pull mechanism.

An independent gossip mechanism is used because it allows for changes to be propogated
more quickly, and also enables us to gossip at a different interval that we perform
failure checks. The gossip rate is tunable, and can be disabled.

A Push/Pull mechanism is also included because it allows new nodes to
get an almost complete member list upon joining. It also is used as
a periodic anti-entropy mechanism to ensure very high convergence rates.
The frequency of this can be adjusted to change the overhead, or disabled
entirely.

*/
package memberlist

import (
	"fmt"
	"net"
	"os"
	"sync"
	"time"
)

type Delegate interface {
	// NodeMeta is used to retrieve meta-data about the current node
	// when broadcasting an alive message. It's length is limited to
	// the given byte size.
	NodeMeta(limit int) []byte

	// NotifyMsg is called when a user-data message is received.
	// This should not block
	NotifyMsg([]byte)

	// GetBroadcasts is called when user data messages can be broadcast.
	// It can return a list of buffers to send. Each buffer should assume an
	// overhead as provided with a limit on the total byte size allowed.
	GetBroadcasts(overhead, limit int) [][]byte

	// LocalState is used for a TCP Push/Pull. This is sent to
	// the remote side as well as membership information
	LocalState() []byte

	// MergeRemoteState is invoked after a TCP Push/Pull. This is the
	// state received from the remote side.
	MergeRemoteState([]byte)
}

type Config struct {
	Name             string        // Node name (FQDN)
	BindAddr         string        // Binding address
	UDPPort          int           // UDP port to listen on
	TCPPort          int           // TCP port to listen on
	TCPTimeout       time.Duration // TCP timeout
	IndirectChecks   int           // Number of indirect checks to use
	RetransmitMult   int           // Retransmits = RetransmitMult * log(N+1)
	SuspicionMult    int           // Suspicion time = SuspcicionMult * log(N+1) * Interval
	PushPullInterval time.Duration // How often we do a Push/Pull update
	RTT              time.Duration // 99% precentile of round-trip-time
	ProbeInterval    time.Duration // Failure probing interval length

	GossipNodes    int           // Number of nodes to gossip to per GossipInterval
	GossipInterval time.Duration // Gossip interval for non-piggyback messages (only if GossipNodes > 0)

	JoinCh       chan<- *Node
	LeaveCh      chan<- *Node
	UserDelegate Delegate // Delegate for user data
}

type Memberlist struct {
	config         *Config
	shutdown       bool
	leave          bool
	leaveBroadcast chan struct{}

	udpListener *net.UDPConn
	tcpListener *net.TCPListener

	sequenceNum uint32 // Local sequence number
	incarnation uint32 // Local incarnation number

	nodeLock sync.RWMutex
	nodes    []*nodeState          // Known nodes
	nodeMap  map[string]*nodeState // Maps Addr.String() -> NodeState

	tickerLock sync.Mutex
	tickers    []*time.Ticker
	stopTick   chan struct{}
	probeIndex int

	ackLock     sync.Mutex
	ackHandlers map[uint32]*ackHandler

	broadcasts *TransmitLimitedQueue
}

// DefaultConfig is used to return a default sane set of configurations for
// Memberlist. It uses the hostname as the node name, and otherwise sets conservative
// values that are sane for most LAN environments.
func DefaultConfig() *Config {
	hostname, _ := os.Hostname()
	return &Config{
		Name:             hostname,
		BindAddr:         "0.0.0.0",
		UDPPort:          7946,
		TCPPort:          7946,
		TCPTimeout:       10 * time.Second,      // Timeout after 10 seconds
		IndirectChecks:   3,                     // Use 3 nodes for the indirect ping
		RetransmitMult:   4,                     // Retransmit a message 4 * log(N+1) nodes
		SuspicionMult:    5,                     // Suspect a node for 5 * log(N+1) * Interval
		PushPullInterval: 30 * time.Second,      // Low frequency
		RTT:              20 * time.Millisecond, // Reasonable RTT time for LAN
		ProbeInterval:    1 * time.Second,       // Failure check every second

		GossipNodes:    3,                      // Gossip to 3 nodes
		GossipInterval: 200 * time.Millisecond, // Gossip more rapidly

		JoinCh:       nil,
		LeaveCh:      nil,
		UserDelegate: nil,
	}
}

// newMemberlist creates the network listeners.
// Does not schedule exeuction of background maintenence.
func newMemberlist(conf *Config) (*Memberlist, error) {
	tcpAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.TCPPort)
	tcpLn, err := net.Listen("tcp", tcpAddr)
	if err != nil {
		return nil, fmt.Errorf("Failed to start TCP listener. Err: %s", err)
	}

	udpAddr := fmt.Sprintf("%s:%d", conf.BindAddr, conf.UDPPort)
	udpLn, err := net.ListenPacket("udp", udpAddr)
	if err != nil {
		tcpLn.Close()
		return nil, fmt.Errorf("Failed to start UDP listener. Err: %s", err)
	}

	// Set the UDP receive window size
	setUDPRecvBuf(udpLn.(*net.UDPConn))

	m := &Memberlist{config: conf,
		leaveBroadcast: make(chan struct{}, 1),
		udpListener:    udpLn.(*net.UDPConn),
		tcpListener:    tcpLn.(*net.TCPListener),
		nodeMap:        make(map[string]*nodeState),
		stopTick:       make(chan struct{}, 32),
		ackHandlers:    make(map[uint32]*ackHandler),
		broadcasts:     &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult},
	}
	m.broadcasts.NumNodes = func() int { return len(m.nodes) }
	go m.tcpListen()
	go m.udpListen()
	return m, nil
}

// Create will start memberlist but does not connect to any other node
func Create(conf *Config) (*Memberlist, error) {
	m, err := newMemberlist(conf)
	if err != nil {
		return nil, err
	}
	if err := m.setAlive(); err != nil {
		m.Shutdown()
		return nil, err
	}
	m.schedule()
	return m, nil
}

// Join is used to take an existing Memberlist and attempt to join
// a cluster by contacting all the given hosts. Returns the number successfully,
// contacted and an error if none could be reached.
func (m *Memberlist) Join(existing []string) (int, error) {
	// Attempt to join any of them
	numSuccess := 0
	var retErr error
	for _, exist := range existing {
		addr, err := net.ResolveIPAddr("ip", exist)
		if err != nil {
			retErr = err
			continue
		}

		if err := m.pushPullNode(addr.IP); err != nil {
			retErr = err
			continue
		}

		numSuccess++
	}

	if numSuccess > 0 {
		retErr = nil
	}
	return numSuccess, retErr
}

// setAlive is used to mark this node as being alive
func (m *Memberlist) setAlive() error {
	// Pick a private IP address
	var ipAddr []byte
	if m.config.BindAddr == "0.0.0.0" {
		// Get the interfaces
		addresses, err := net.InterfaceAddrs()
		if err != nil {
			return fmt.Errorf("Failed to get interface addresses! Err: %vn", err)
		}

		// Find private IPv4 address
		for _, addr := range addresses {
			ip, ok := addr.(*net.IPNet)
			if !ok {
				continue
			}
			if ip.IP.To4() == nil {
				continue
			}
			if !isPrivateIP(ip.IP.String()) {
				continue
			}
			ipAddr = ip.IP
			break
		}

		// Failed to find private IP, use loopback
		if ipAddr == nil {
			ipAddr = []byte{127, 0, 0, 1}
		}

	} else {
		addr := m.tcpListener.Addr().(*net.TCPAddr)
		ipAddr = addr.IP
	}

	// Get the node meta data
	var meta []byte
	if m.config.UserDelegate != nil {
		meta = m.config.UserDelegate.NodeMeta(metaMaxSize)
		if len(meta) > metaMaxSize {
			panic("Node meta data provided is longer than the limit")
		}
	}

	a := alive{
		Incarnation: m.nextIncarnation(),
		Node:        m.config.Name,
		Addr:        ipAddr,
		Meta:        meta,
	}
	m.aliveNode(&a)
	return nil
}

// Members is used to return a list of all known live nodes
func (m *Memberlist) Members() []*Node {
	nodes := make([]*Node, 0, len(m.nodes))
	m.nodeLock.RLock()
	defer m.nodeLock.RUnlock()
	for _, n := range m.nodes {
		if n.State != stateDead {
			nodes = append(nodes, &n.Node)
		}
	}
	return nodes
}

// NumMembers provides an efficient way to determine
// the number of alive members
func (m *Memberlist) NumMembers() (alive int) {
	m.nodeLock.RLock()
	defer m.nodeLock.RUnlock()
	for _, n := range m.nodes {
		if n.State != stateDead {
			alive++
		}
	}
	return
}

// Leave will broadcast a leave message but will not shutdown
// the memberlist background maintenence. This should be followed
// by a Shutdown(). This will block until the death message has
// finished gossiping out.
func (m *Memberlist) Leave() error {
	m.leave = true
	d := dead{Incarnation: m.incarnation, Node: m.config.Name}
	m.deadNode(&d)

	// Block until the broadcast goes out
	if len(m.nodes) > 1 {
		<-m.leaveBroadcast
	}
	return nil
}

// Shutdown will stop the memberlist background maintenence
// but will not broadcast a leave message prior. If no prior
// leave was issued, other nodes will detect this as a failure.
func (m *Memberlist) Shutdown() error {
	m.shutdown = true
	m.deschedule()
	m.udpListener.Close()
	m.tcpListener.Close()
	return nil
}
<|endoftext|>


<commit_before>package chalmers_chop

import (
	"errors"
	"github.com/mmcdole/gofeed"
	"strconv"
	"strings"
	"time"
)

// Defines the mapping between  sources and allergens
var allergenImages map[string]Allergen = map[string]Allergen{
	"gluten-white.png": Gluten,
	"gluten-black.png": Gluten,
	"egg-white.png":    Egg,
	"egg-black.png":    Egg,
	"lactose-white":    Lactose,
	"lactose-black":    Lactose,
	"FISH_black.png":   Fish,
	"FISH_white.png":   Fish,
}

func ParseDailyMenu(feed *gofeed.Feed) *Menu {
	var menu Menu

	menu.Title = feed.Title
	menu.Date = getMenuDate(feed)

	for _, item := range feed.Items {
		menu.AddDish(parseDish(item))
	}

	return &menu
}

func getMenuDate(feed *gofeed.Feed) string {
	d, err := parseMenuDate(feed)

	if err != nil {
		// Default to today's date
		return time.Now().Format("2006-01-02")
	}

	return d
}

// parseMenuDate parses the date of a menu.
//
// The menu date is retrieved by parsing the contents of the <guid> tag and fetching
// the last 10 runes, which will always be a date on the format YYYY-mm-dd
func parseMenuDate(feed *gofeed.Feed) (string, error) {
	if len(feed.Items) == 0 {
		return "", errors.New("Could not find menu date")
	}

	t := feed.Items[0].GUID

	return string(t[len(t)-10:]), nil
}

// parseDish parses a dish from a feed item.
//
// The dish contents, price, and allergy information is contained in a CDATA tag inside the <description> tag. For example:
//
// <description>
// <![CDATA[Beef, wheat bread, french fries@80
   >

]]>
//          ^                               ^                                                                                ^
//          contents                        price                                                                             allergen (Gluten)
//</description>
func parseDish(item *gofeed.Item) Dish {
	var dish Dish

	dish.Name = item.Title

	desc := trimCDATA(item.Description)

	dish.Contents = parseContents(desc)
	dish.Price = parsePrice(desc)
	dish.Allergens = parseAllergens(desc)

	return dish
}

// parseContents parses a dish's contents from its description text.
//
// The dish contents is all text content leading up to the @ sign.
func parseContents(desc string) string {
	return strings.Split(desc, "@")[0]
}

// parsePrice parses the price of a dish from its description text.
//
// The dish price is all text content following the first @ sign leading up to
// the first space sign.
func parsePrice(desc string) int {
	afterAtSign := strings.Split(desc, "@")[1]
	priceText := beforeSpace(afterAtSign)

	p, err := strconv.Atoi(priceText)

	if err != nil {
		return 0
	}

	return p
}

// parseAllergens parses the allergens of a dish from its description text.
//
// The dish allergens can be determined by searching the description text for
// occurrences of image sources defined in allergenImages.
//
// For example, the following dish description indicates that the dish contains the Gluten allergen ("gluten-white.png"):
//
// <![CDATA[Hamburger of the Day@80
   >

]]>
func parseAllergens(desc string) []Allergen {
	var al []Allergen

	for k, a := range allergenImages {
		if strings.Contains(desc, k) {
			al = append(al, a)
		}
	}

	return al
}

func trimCDATA(text string) string {
	if !strings.HasPrefix(text, "<![CDATA") || !strings.HasSuffix(text, "]]>") {
		return text
	}

	return strings.TrimSuffix(strings.TrimPrefix(text, "<![CDATA"), "]]>")
}

// beforeSpace returns a substring of str, starting at the beginning of str
// and ending right before the first occurrence of a space character (0x20).
//
// Returns str unmodified if str does not contain any spaces.
func beforeSpace(str string) string {
	indexFunc := func(r rune) bool {
		return r == ' '
	}

	i := strings.IndexFunc(str, indexFunc)

	if i >= 0 && i <= len(str) {
		return str[:i]
	}

	return str
}
<commit_msg>Rename trimCDATA to trimCDATATags for correctness<commit_after>package chalmers_chop

import (
	"errors"
	"github.com/mmcdole/gofeed"
	"strconv"
	"strings"
	"time"
)

// Defines the mapping between  sources and allergens
var allergenImages map[string]Allergen = map[string]Allergen{
	"gluten-white.png": Gluten,
	"gluten-black.png": Gluten,
	"egg-white.png":    Egg,
	"egg-black.png":    Egg,
	"lactose-white":    Lactose,
	"lactose-black":    Lactose,
	"FISH_black.png":   Fish,
	"FISH_white.png":   Fish,
}

func ParseDailyMenu(feed *gofeed.Feed) *Menu {
	var menu Menu

	menu.Title = feed.Title
	menu.Date = getMenuDate(feed)

	for _, item := range feed.Items {
		menu.AddDish(parseDish(item))
	}

	return &menu
}

func getMenuDate(feed *gofeed.Feed) string {
	d, err := parseMenuDate(feed)

	if err != nil {
		// Default to today's date
		return time.Now().Format("2006-01-02")
	}

	return d
}

// parseMenuDate parses the date of a menu.
//
// The menu date is retrieved by parsing the contents of the <guid> tag and fetching
// the last 10 runes, which will always be a date on the format YYYY-mm-dd
func parseMenuDate(feed *gofeed.Feed) (string, error) {
	if len(feed.Items) == 0 {
		return "", errors.New("Could not find menu date")
	}

	t := feed.Items[0].GUID

	return string(t[len(t)-10:]), nil
}

// parseDish parses a dish from a feed item.
//
// The dish contents, price, and allergy information is contained in a CDATA tag inside the <description> tag. For example:
//
// <description>
// <![CDATA[Beef, wheat bread, french fries@80
   >

]]>
//          ^                               ^                                                                                ^
//          contents                        price                                                                             allergen (Gluten)
//</description>
func parseDish(item *gofeed.Item) Dish {
	var dish Dish

	dish.Name = item.Title

	desc := trimCDATATags(item.Description)

	dish.Contents = parseContents(desc)
	dish.Price = parsePrice(desc)
	dish.Allergens = parseAllergens(desc)

	return dish
}

// parseContents parses a dish's contents from its description text.
//
// The dish contents is all text content leading up to the @ sign.
func parseContents(desc string) string {
	return strings.Split(desc, "@")[0]
}

// parsePrice parses the price of a dish from its description text.
//
// The dish price is all text content following the first @ sign leading up to
// the first space sign.
func parsePrice(desc string) int {
	afterAtSign := strings.Split(desc, "@")[1]
	priceText := beforeSpace(afterAtSign)

	p, err := strconv.Atoi(priceText)

	if err != nil {
		return 0
	}

	return p
}

// parseAllergens parses the allergens of a dish from its description text.
//
// The dish allergens can be determined by searching the description text for
// occurrences of image sources defined in allergenImages.
//
// For example, the following dish description indicates that the dish contains the Gluten allergen ("gluten-white.png"):
//
// <![CDATA[Hamburger of the Day@80
   >

]]>
func parseAllergens(desc string) []Allergen {
	var al []Allergen

	for k, a := range allergenImages {
		if strings.Contains(desc, k) {
			al = append(al, a)
		}
	}

	return al
}

func trimCDATATags(text string) string {
	if !strings.HasPrefix(text, "<![CDATA") || !strings.HasSuffix(text, "]]>") {
		return text
	}

	return strings.TrimSuffix(strings.TrimPrefix(text, "<![CDATA"), "]]>")
}

// beforeSpace returns a substring of str, starting at the beginning of str
// and ending right before the first occurrence of a space character (0x20).
//
// Returns str unmodified if str does not contain any spaces.
func beforeSpace(str string) string {
	indexFunc := func(r rune) bool {
		return r == ' '
	}

	i := strings.IndexFunc(str, indexFunc)

	if i >= 0 && i <= len(str) {
		return str[:i]
	}

	return str
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/cli/command/commands"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"github.com/spf13/pflag"
)

const descriptionSourcePath = "man/src/"

func generateManPages(opts *options) error {
	header := &doc.GenManHeader{
		Title:   "DOCKER",
		Section: "1",
		Source:  "Docker Community",
		Manual:  "Docker User Manuals",
	}

	// If SOURCE_DATE_EPOCH is set, in order to allow reproducible package
	// builds, we explicitly set the build time to SOURCE_DATE_EPOCH.
	if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" {
		unixEpoch, err := strconv.ParseInt(epoch, 10, 64)
		if err != nil {
			return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err)
		}
		now := time.Unix(unixEpoch, 0)
		header.Date = &now
	}

	dockerCli, err := command.NewDockerCli()
	if err != nil {
		return err
	}
	cmd := &cobra.Command{Use: "docker"}
	commands.AddCommands(cmd, dockerCli)
	source := filepath.Join(opts.source, descriptionSourcePath)
	if err := loadLongDescription(cmd, source); err != nil {
		return err
	}

	cmd.DisableAutoGenTag = true
	cmd.DisableFlagsInUseLine = true
	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
		Header:           header,
		Path:             opts.target,
		CommandSeparator: "-",
	})
}

func loadLongDescription(cmd *cobra.Command, path string) error {
	for _, cmd := range cmd.Commands() {
		cmd.DisableFlagsInUseLine = true
		if cmd.Name() == "" {
			continue
		}
		fullpath := filepath.Join(path, cmd.Name()+".md")

		if cmd.HasSubCommands() {
			loadLongDescription(cmd, filepath.Join(path, cmd.Name()))
		}

		if _, err := os.Stat(fullpath); err != nil {
			log.Printf("WARN: %s does not exist, skipping\n", fullpath)
			continue
		}

		content, err := ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Long = string(content)

		fullpath = filepath.Join(path, cmd.Name()+"-example.md")
		if _, err := os.Stat(fullpath); err != nil {
			continue
		}

		content, err = ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Example = string(content)

	}
	return nil
}

type options struct {
	source string
	target string
}

func parseArgs() (*options, error) {
	opts := &options{}
	cwd, _ := os.Getwd()
	flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
	flags.StringVar(&opts.source, "root", cwd, "Path to project root")
	flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages")
	err := flags.Parse(os.Args[1:])
	return opts, err
}

func main() {
	opts, err := parseArgs()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}
	fmt.Printf("Project root: %s\n", opts.source)
	fmt.Printf("Generating man pages into %s\n", opts.target)
	if err := generateManPages(opts); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error())
	}
}
<commit_msg>man: remove deprecated io/ioutil and use t.TempDir()<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/cli/command/commands"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"github.com/spf13/pflag"
)

const descriptionSourcePath = "man/src/"

func generateManPages(opts *options) error {
	header := &doc.GenManHeader{
		Title:   "DOCKER",
		Section: "1",
		Source:  "Docker Community",
		Manual:  "Docker User Manuals",
	}

	// If SOURCE_DATE_EPOCH is set, in order to allow reproducible package
	// builds, we explicitly set the build time to SOURCE_DATE_EPOCH.
	if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" {
		unixEpoch, err := strconv.ParseInt(epoch, 10, 64)
		if err != nil {
			return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err)
		}
		now := time.Unix(unixEpoch, 0)
		header.Date = &now
	}

	dockerCli, err := command.NewDockerCli()
	if err != nil {
		return err
	}
	cmd := &cobra.Command{Use: "docker"}
	commands.AddCommands(cmd, dockerCli)
	source := filepath.Join(opts.source, descriptionSourcePath)
	if err := loadLongDescription(cmd, source); err != nil {
		return err
	}

	cmd.DisableAutoGenTag = true
	cmd.DisableFlagsInUseLine = true
	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
		Header:           header,
		Path:             opts.target,
		CommandSeparator: "-",
	})
}

func loadLongDescription(cmd *cobra.Command, path string) error {
	for _, cmd := range cmd.Commands() {
		cmd.DisableFlagsInUseLine = true
		if cmd.Name() == "" {
			continue
		}
		fullpath := filepath.Join(path, cmd.Name()+".md")

		if cmd.HasSubCommands() {
			loadLongDescription(cmd, filepath.Join(path, cmd.Name()))
		}

		if _, err := os.Stat(fullpath); err != nil {
			log.Printf("WARN: %s does not exist, skipping\n", fullpath)
			continue
		}

		content, err := os.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Long = string(content)

		fullpath = filepath.Join(path, cmd.Name()+"-example.md")
		if _, err := os.Stat(fullpath); err != nil {
			continue
		}

		content, err = os.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Example = string(content)

	}
	return nil
}

type options struct {
	source string
	target string
}

func parseArgs() (*options, error) {
	opts := &options{}
	cwd, _ := os.Getwd()
	flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
	flags.StringVar(&opts.source, "root", cwd, "Path to project root")
	flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages")
	err := flags.Parse(os.Args[1:])
	return opts, err
}

func main() {
	opts, err := parseArgs()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}
	fmt.Printf("Project root: %s\n", opts.source)
	fmt.Printf("Generating man pages into %s\n", opts.target)
	if err := generateManPages(opts); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error())
	}
}
<|endoftext|>


<commit_before>package brackets

// Source: exercism/problem-specifications
// Commit: 5c16d05 bracket-push: Apply new "input" policy
// Problem Specifications Version: 1.2.0

type bracketTest struct {
	input    string
	expected bool
}

var testCases = []bracketTest{
	{
		// paired square brackets
		"[]",
		true,
	},
	{
		// empty string
		"",
		true,
	},
	{
		// unpaired brackets
		"[[",
		false,
	},
	{
		// wrong ordered brackets
		"}{",
		false,
	},
	{
		// wrong closing bracket
		"{]",
		false,
	},
	{
		// paired with whitespace
		"{ }",
		true,
	},
	{
		// simple nested brackets
		"{[]}",
		true,
	},
	{
		// several paired brackets
		"{}[]",
		true,
	},
	{
		// paired and nested brackets
		"([{}({}[])])",
		true,
	},
	{
		// unopened closing brackets
		"{[)][]}",
		false,
	},
	{
		// unpaired and nested brackets
		"([{])",
		false,
	},
	{
		// paired and wrong nested brackets
		"[({]})",
		false,
	},
	{
		// math expression
		"(((185 + 223.85) * 15) - 543)/2",
		true,
	},
	{
		// complex latex expression
		"\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)",
		true,
	},
}
<commit_msg>bracket-push: update test cases<commit_after>package brackets

// Source: exercism/problem-specifications
// Commit: 9027186 bracket-push: Add test case "partially paired brackets" (#1219)
// Problem Specifications Version: 1.3.0

type bracketTest struct {
	input    string
	expected bool
}

var testCases = []bracketTest{
	{
		// paired square brackets
		"[]",
		true,
	},
	{
		// empty string
		"",
		true,
	},
	{
		// unpaired brackets
		"[[",
		false,
	},
	{
		// wrong ordered brackets
		"}{",
		false,
	},
	{
		// wrong closing bracket
		"{]",
		false,
	},
	{
		// paired with whitespace
		"{ }",
		true,
	},
	{
		// partially paired brackets
		"{[])",
		false,
	},
	{
		// simple nested brackets
		"{[]}",
		true,
	},
	{
		// several paired brackets
		"{}[]",
		true,
	},
	{
		// paired and nested brackets
		"([{}({}[])])",
		true,
	},
	{
		// unopened closing brackets
		"{[)][]}",
		false,
	},
	{
		// unpaired and nested brackets
		"([{])",
		false,
	},
	{
		// paired and wrong nested brackets
		"[({]})",
		false,
	},
	{
		// math expression
		"(((185 + 223.85) * 15) - 543)/2",
		true,
	},
	{
		// complex latex expression
		"\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)",
		true,
	},
}
<|endoftext|>