<commit_before>package translator

import (
	"strconv"
	"strings"

	"github.com/bkidney/EQ2Dot/syntaxTree"
	"github.com/bkidney/gofelex"
)

type Translator struct {
	ast            *syntaxTree.SyntaxTree
	nodeNumber     int
	subgraphNumber int
}

func NewTranslator(ast *syntaxTree.SyntaxTree) *Translator {
	translator := &Translator{}
	translator.ast = ast
	translator.nodeNumber = 2 // 2 is first node after query
	translator.subgraphNumber = 0

	return translator
}

func (translator *Translator) Dot() string {
	var out string

	out = translator.generatePreamble()
	out = out + translator.generateDot(translator.ast.GetChild())
	out = out + translator.generatePostamble()
	return out
}

func (translator *Translator) generatePreamble() string {
	out := "digraph query {\n\tnode [shape = none];\n\t0 [label = \"start\"];\n\tnode [shape = circle];\n\t0 -> 1;\n"
	return out
}

func (translator *Translator) generatePostamble() string {
	return "}"
}

func (translator *Translator) generateDot(ast *syntaxTree.SyntaxTree) string {
	var out string
	var currNode = ast.GetNode()

	// Here we look at the node
	//  If binary operator, get child translation and child.sibling translation
	//  Combine as necessay
	//  Withins are boxed
	if currNode.NodeType == gofelex.TEMPORAL {
		temporalType := strings.ToUpper(currNode.Literal)
		if temporalType == "WITHIN" {
			out = translator.createSubGraph(ast.GetChild())
		} else {
			lhs := ast.GetChild()

			if lhs.GetNode().NodeType == gofelex.IDENT {
				edge := ast.GetChild().GetNode().Literal
				nodeNum := translator.getNodeNum()
				next := translator.generateDot(ast.GetChild().GetSibling())

				if next == "" {
					out = translator.createFinalNode(edge, nodeNum)
				} else {
					out = translator.createNode(edge, nodeNum) + next
				}
			} else {
				out = translator.generateDot(lhs) + translator.generateDot(lhs.GetSibling())
			}
		}

	}
	// startSubgraph(left, right)
	//  and is literal -> literal
	if currNode.NodeType == gofelex.LOGICAL {
		edge := ast.GetChild().GetNode().Literal
		nodeNum := translator.getNodeNum()
		next := translator.generateDot(ast.GetChild().GetSibling())

		if next == "" {
			out = translator.createFinalNode(edge, nodeNum)
		} else {
			out = translator.createNode(edge, nodeNum) + next
		}
	}
	//  or is parallel linking
	//  flow combines literals
	if currNode.NodeType == gofelex.FLOW {
		lhs := ast.GetChild().GetNode().Literal
		op := currNode.Literal
		rhs := ast.GetChild().GetSibling().GetNode().Literal

		edge := lhs + " " + op + " " + rhs

		out = translator.createFinalNode(edge, translator.getNodeNum())
	}
	//  condition combinines literal
	if currNode.NodeType == gofelex.CONDITION {
		lhs := ast.GetChild().GetNode().Literal
		op := currNode.Literal
		rhs := ast.GetChild().GetSibling().GetNode().Literal

		edge := lhs + " " + op + " " + rhs

		out = translator.createFinalNode(edge, translator.getNodeNum())
	}

	if currNode.NodeType == gofelex.IDENT {
		nodeNum := translator.getNodeNum()
		out = translator.createNode(currNode.Literal, nodeNum)
	}

	return out
}

func (translator *Translator) createNode(edge string, nodeNum int) string {
	var out string

	out = "\t" + strconv.Itoa(nodeNum-1) + " -> " + strconv.Itoa(nodeNum) + " [label = \"" + edge + "\"];\n"

	return out
}

func (translator *Translator) createFinalNode(edge string, nodeNum int) string {
	var out string

	out = "\tnode [shape = doublecircle];\n"
	out = out + translator.createNode(edge, nodeNum)

	return out
}

func (translator *Translator) createSubGraph(ast *syntaxTree.SyntaxTree) string {
	var out string
	enterEdge := "Call - " + ast.GetNode().Literal
	exitEdge := "Ret - " + ast.GetNode().Literal

	out = translator.createNode(enterEdge, translator.getNodeNum())
	out = out + "\tsubgraph cluster_" + strconv.Itoa(translator.getSubgraphNum()) + " {\n\trank = same;\n\tstyle=\"dashed\";\n"
	out = out + translator.generateDot(ast.GetSibling())
	out = out + "\t}"

	out = out + translator.createNode(exitEdge, translator.getNodeNum())

	return out
}

func (translator *Translator) getNodeNum() int {
	ret := translator.nodeNumber
	translator.nodeNumber++
	return ret
}

func (translator *Translator) getSubgraphNum() int {
	ret := translator.subgraphNumber
	translator.subgraphNumber++
	return ret
}
<commit_msg>Cleans up the node numbering and final node gen.<commit_after>package translator

import (
	"strconv"
	"strings"

	"github.com/bkidney/EQ2Dot/syntaxTree"
	"github.com/bkidney/gofelex"
)

type Translator struct {
	ast            *syntaxTree.SyntaxTree
	nodeNumber     int
	subgraphNumber int
}

func NewTranslator(ast *syntaxTree.SyntaxTree) *Translator {
	translator := &Translator{}
	translator.ast = ast
	translator.nodeNumber = 2 // 2 is first node after query
	translator.subgraphNumber = 0

	return translator
}

func (translator *Translator) Dot() string {
	var out string

	out = translator.generatePreamble()
	out = out + translator.generateDot(translator.ast.GetChild())
	out = out + translator.generatePostamble()
	return out
}

func (translator *Translator) generatePreamble() string {
	out := "digraph query {\n\tnode [shape = none];\n\t0 [label = \"start\"];\n\tnode [shape = circle];\n\t0 -> 1;\n"
	return out
}

func (translator *Translator) generatePostamble() string {
	lastNode := translator.getLastNode()
	return "\t" + strconv.Itoa(lastNode) + " [shape = doublecircle];\n}"
}

func (translator *Translator) generateDot(ast *syntaxTree.SyntaxTree) string {
	var out string
	var currNode = ast.GetNode()

	// Here we look at the node
	//  If binary operator, get child translation and child.sibling translation
	//  Combine as necessay
	//  Withins are boxed
	if currNode.NodeType == gofelex.TEMPORAL {
		temporalType := strings.ToUpper(currNode.Literal)
		if temporalType == "WITHIN" {
			out = translator.createSubGraph(ast.GetChild())
		} else {
			lhs := ast.GetChild()

			if lhs.GetNode().NodeType == gofelex.IDENT {
				edge := ast.GetChild().GetNode().Literal
				nodeNum := translator.getNextNode()
				next := translator.generateDot(ast.GetChild().GetSibling())

				out = translator.createNode(edge, nodeNum) + next
			} else {
				out = translator.generateDot(lhs) + translator.generateDot(lhs.GetSibling())
			}
		}

	}
	// startSubgraph(left, right)
	//  and is literal -> literal
	if currNode.NodeType == gofelex.LOGICAL {
		if currNode.Literal == "and" {
			edge := ast.GetChild().GetNode().Literal
			nodeNum := translator.getNextNode()
			next := translator.generateDot(ast.GetChild().GetSibling())

			out = translator.createNode(edge, nodeNum) + next
		}
	}
	//  or is parallel linking
	//  flow combines literals
	if currNode.NodeType == gofelex.FLOW {
		lhs := ast.GetChild().GetNode().Literal
		op := currNode.Literal
		rhs := ast.GetChild().GetSibling().GetNode().Literal

		edge := lhs + " " + op + " " + rhs

		out = translator.createNode(edge, translator.getNextNode())
	}
	//  condition combinines literal
	if currNode.NodeType == gofelex.CONDITION {
		lhs := ast.GetChild().GetNode().Literal
		op := currNode.Literal
		rhs := ast.GetChild().GetSibling().GetNode().Literal

		edge := lhs + " " + op + " " + rhs

		out = translator.createNode(edge, translator.getNextNode())
	}

	if currNode.NodeType == gofelex.IDENT {
		nodeNum := translator.getNextNode()
		out = translator.createNode(currNode.Literal, nodeNum)
	}

	return out
}

func (translator *Translator) createNode(edge string, nodeNum int) string {
	return translator.createNodeEx(edge, nodeNum-1, nodeNum)
}

func (translator *Translator) createNodeEx(edge string, startNode int, endNode int) string {
	var out string

	out = "\t" + strconv.Itoa(startNode) + " -> " + strconv.Itoa(endNode) + " [label = \"" + edge + "\"];\n"

	return out
}

func (translator *Translator) createSubGraph(ast *syntaxTree.SyntaxTree) string {
	var out string
	enterEdge := "Call - " + ast.GetNode().Literal
	exitEdge := "Ret - " + ast.GetNode().Literal

	out = translator.createNode(enterEdge, translator.getNextNode())
	out = out + "\tsubgraph cluster_" + strconv.Itoa(translator.getSubgraphNum()) + " {\n\trank = same;\n\tstyle=\"dashed\";\n"
	out = out + translator.generateDot(ast.GetSibling())
	out = out + "\t}"

	out = out + translator.createNode(exitEdge, translator.getNextNode())

	return out
}

func (translator *Translator) getNextNode() int {
	ret := translator.nodeNumber
	translator.nodeNumber++
	return ret
}

func (translator *Translator) getLastNode() int {
	return translator.nodeNumber - 1
}

func (translator *Translator) getSubgraphNum() int {
	ret := translator.subgraphNumber
	translator.subgraphNumber++
	return ret
}
<|endoftext|>


<commit_before>package revel

import (
	"log"
	"os"
	"path/filepath"
	"reflect"
)

type Hotel struct {
	HotelID          int
	Name, Address    string
	City, State, Zip string
	Country          string
	Price            int
}

type Hotels struct {
	*Controller
}

type Static struct {
	*Controller
}

func (c Hotels) Show(id int) Result {
	title := "View Hotel"
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.Render(title, hotel)
}

func (c Hotels) Book(id int) Result {
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.RenderJSON(hotel)
}

func (c Hotels) Index() Result {
	return c.RenderText("Hello, World!")
}

func (c Static) Serve(prefix, path string) Result {
	var basePath, dirName string

	if !filepath.IsAbs(dirName) {
		basePath = BasePath
	}

	fname := filepath.Join(basePath, prefix, path)
	file, err := os.Open(fname)
	if os.IsNotExist(err) {
		return c.NotFound("")
	} else if err != nil {
		WARN.Printf("Problem opening file (%s): %s ", fname, err)
		return c.NotFound("This was found but not sure why we couldn't open it.")
	}
	return c.RenderFile(file, "")
}

func startFakeBookingApp() {
	Init("prod", "github.com/revel/revel/testdata", "")

	// Disable logging.
	TRACE = log.New(os.Stderr, "", 0)
	INFO = TRACE
	WARN = TRACE
	ERROR = TRACE

	MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
	if err := MainTemplateLoader.Refresh(); err != nil {
		ERROR.Fatal(err)
	}

	RegisterController((*Hotels)(nil),
		[]*MethodType{
			{
				Name: "Index",
			},
			{
				Name: "Show",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
				RenderArgNames: map[int][]string{29: {"title", "hotel"}},
			},
			{
				Name: "Book",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
			},
		})

	RegisterController((*Static)(nil),
		[]*MethodType{
			{
				Name: "Serve",
				Args: []*MethodArg{
					{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
					{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
				},
				RenderArgNames: map[int][]string{},
			},
		})

	runStartupHooks()
}
<commit_msg>test case update for #1057<commit_after>package revel

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
)

type Hotel struct {
	HotelID          int
	Name, Address    string
	City, State, Zip string
	Country          string
	Price            int
}

type Hotels struct {
	*Controller
}

type Static struct {
	*Controller
}

func (c Hotels) Show(id int) Result {
	title := "View Hotel"
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.Render(title, hotel)
}

func (c Hotels) Book(id int) Result {
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.RenderJSON(hotel)
}

func (c Hotels) Index() Result {
	return c.RenderText("Hello, World!")
}

func (c Static) Serve(prefix, path string) Result {
	var basePath, dirName string

	if !filepath.IsAbs(dirName) {
		basePath = BasePath
	}

	fname := filepath.Join(basePath, prefix, path)
	file, err := os.Open(fname)
	if os.IsNotExist(err) {
		return c.NotFound("")
	} else if err != nil {
		WARN.Printf("Problem opening file (%s): %s ", fname, err)
		return c.NotFound("This was found but not sure why we couldn't open it.")
	}
	return c.RenderFile(file, "")
}

func startFakeBookingApp() {
	Init("prod", "github.com/revel/revel/testdata", "")

	// Disable logging.
	TRACE = log.New(ioutil.Discard, "", 0)
	INFO = TRACE
	WARN = TRACE
	ERROR = TRACE

	MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
	if err := MainTemplateLoader.Refresh(); err != nil {
		ERROR.Fatal(err)
	}

	RegisterController((*Hotels)(nil),
		[]*MethodType{
			{
				Name: "Index",
			},
			{
				Name: "Show",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
				RenderArgNames: map[int][]string{30: {"title", "hotel"}},
			},
			{
				Name: "Book",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
			},
		})

	RegisterController((*Static)(nil),
		[]*MethodType{
			{
				Name: "Serve",
				Args: []*MethodArg{
					{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
					{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
				},
				RenderArgNames: map[int][]string{},
			},
		})

	runStartupHooks()
}
<|endoftext|>


<commit_before>// Copyright Istio 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 protocol

import "strings"

// Instance defines network protocols for ports
type Instance string

const (
	// GRPC declares that the port carries gRPC traffic.
	GRPC Instance = "GRPC"
	// GRPCWeb declares that the port carries gRPC traffic.
	GRPCWeb Instance = "GRPC-Web"
	// HTTP declares that the port carries HTTP/1.1 traffic.
	// Note that HTTP/1.0 or earlier may not be supported by the proxy.
	HTTP Instance = "HTTP"
	// HTTP_PROXY declares that the port is a generic outbound proxy port.
	// Note that this is currently applicable only for defining sidecar egress listeners.
	// nolint
	HTTP_PROXY Instance = "HTTP_PROXY"
	// HTTP2 declares that the port carries HTTP/2 traffic.
	HTTP2 Instance = "HTTP2"
	// HTTPS declares that the port carries HTTPS traffic.
	HTTPS Instance = "HTTPS"
	// Thrift declares that the port carries Thrift traffic.
	Thrift Instance = "Thrift"
	// TCP declares the the port uses TCP.
	// This is the default protocol for a service port.
	TCP Instance = "TCP"
	// TLS declares that the port carries TLS traffic.
	// TLS traffic is assumed to contain SNI as part of the handshake.
	TLS Instance = "TLS"
	// UDP declares that the port uses UDP.
	// Note that UDP protocol is not currently supported by the proxy.
	UDP Instance = "UDP"
	// Mongo declares that the port carries MongoDB traffic.
	Mongo Instance = "Mongo"
	// Redis declares that the port carries Redis traffic.
	Redis Instance = "Redis"
	// MySQL declares that the port carries MySQL traffic.
	MySQL Instance = "MySQL"
	// Unsupported - value to signify that the protocol is unsupported.
	Unsupported Instance = "UnsupportedProtocol"
)

// Parse from string ignoring case
func Parse(s string) Instance {
	switch strings.ToLower(s) {
	case "tcp":
		return TCP
	case "udp":
		return UDP
	case "grpc":
		return GRPC
	case "grpc-web":
		return GRPCWeb
	case "http":
		return HTTP
	case "http_proxy":
		return HTTP_PROXY
	case "http2":
		return HTTP2
	case "https":
		return HTTPS
	case "thrift":
		return Thrift
	case "tls":
		return TLS
	case "mongo":
		return Mongo
	case "redis":
		return Redis
	case "mysql":
		return MySQL
	}

	return Unsupported
}

// IsHTTP2 is true for protocols that use HTTP/2 as transport protocol
func (i Instance) IsHTTP2() bool {
	switch i {
	case HTTP2, GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

// IsHTTP is true for protocols that use HTTP as transport protocol
func (i Instance) IsHTTP() bool {
	switch i {
	case HTTP, HTTP2, HTTP_PROXY, GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

// IsThrift is true for protocols that use Thrift as transport protocol
func (i Instance) IsThrift() bool {
	switch i {
	case Thrift:
		return true
	default:
		return false
	}
}

// IsTCP is true for protocols that use TCP as transport protocol
func (i Instance) IsTCP() bool {
	switch i {
	case TCP, HTTPS, TLS, Mongo, Redis, MySQL, Thrift:
		return true
	default:
		return false
	}
}

// IsTLS is true for protocols on top of TLS (e.g. HTTPS)
func (i Instance) IsTLS() bool {
	switch i {
	case HTTPS, TLS:
		return true
	default:
		return false
	}
}

// IsGRPC is true for GRCP protocols.
func (i Instance) IsGRPC() bool {
	switch i {
	case GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

func (i Instance) IsUnsupported() bool {
	return i == Unsupported
}
<commit_msg>Fix spelling mistakes (#27197)<commit_after>// Copyright Istio 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 protocol

import "strings"

// Instance defines network protocols for ports
type Instance string

const (
	// GRPC declares that the port carries gRPC traffic.
	GRPC Instance = "GRPC"
	// GRPCWeb declares that the port carries gRPC traffic.
	GRPCWeb Instance = "GRPC-Web"
	// HTTP declares that the port carries HTTP/1.1 traffic.
	// Note that HTTP/1.0 or earlier may not be supported by the proxy.
	HTTP Instance = "HTTP"
	// HTTP_PROXY declares that the port is a generic outbound proxy port.
	// Note that this is currently applicable only for defining sidecar egress listeners.
	// nolint
	HTTP_PROXY Instance = "HTTP_PROXY"
	// HTTP2 declares that the port carries HTTP/2 traffic.
	HTTP2 Instance = "HTTP2"
	// HTTPS declares that the port carries HTTPS traffic.
	HTTPS Instance = "HTTPS"
	// Thrift declares that the port carries Thrift traffic.
	Thrift Instance = "Thrift"
	// TCP declares the the port uses TCP.
	// This is the default protocol for a service port.
	TCP Instance = "TCP"
	// TLS declares that the port carries TLS traffic.
	// TLS traffic is assumed to contain SNI as part of the handshake.
	TLS Instance = "TLS"
	// UDP declares that the port uses UDP.
	// Note that UDP protocol is not currently supported by the proxy.
	UDP Instance = "UDP"
	// Mongo declares that the port carries MongoDB traffic.
	Mongo Instance = "Mongo"
	// Redis declares that the port carries Redis traffic.
	Redis Instance = "Redis"
	// MySQL declares that the port carries MySQL traffic.
	MySQL Instance = "MySQL"
	// Unsupported - value to signify that the protocol is unsupported.
	Unsupported Instance = "UnsupportedProtocol"
)

// Parse from string ignoring case
func Parse(s string) Instance {
	switch strings.ToLower(s) {
	case "tcp":
		return TCP
	case "udp":
		return UDP
	case "grpc":
		return GRPC
	case "grpc-web":
		return GRPCWeb
	case "http":
		return HTTP
	case "http_proxy":
		return HTTP_PROXY
	case "http2":
		return HTTP2
	case "https":
		return HTTPS
	case "thrift":
		return Thrift
	case "tls":
		return TLS
	case "mongo":
		return Mongo
	case "redis":
		return Redis
	case "mysql":
		return MySQL
	}

	return Unsupported
}

// IsHTTP2 is true for protocols that use HTTP/2 as transport protocol
func (i Instance) IsHTTP2() bool {
	switch i {
	case HTTP2, GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

// IsHTTP is true for protocols that use HTTP as transport protocol
func (i Instance) IsHTTP() bool {
	switch i {
	case HTTP, HTTP2, HTTP_PROXY, GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

// IsThrift is true for protocols that use Thrift as transport protocol
func (i Instance) IsThrift() bool {
	switch i {
	case Thrift:
		return true
	default:
		return false
	}
}

// IsTCP is true for protocols that use TCP as transport protocol
func (i Instance) IsTCP() bool {
	switch i {
	case TCP, HTTPS, TLS, Mongo, Redis, MySQL, Thrift:
		return true
	default:
		return false
	}
}

// IsTLS is true for protocols on top of TLS (e.g. HTTPS)
func (i Instance) IsTLS() bool {
	switch i {
	case HTTPS, TLS:
		return true
	default:
		return false
	}
}

// IsGRPC is true for GRPC protocols.
func (i Instance) IsGRPC() bool {
	switch i {
	case GRPC, GRPCWeb:
		return true
	default:
		return false
	}
}

func (i Instance) IsUnsupported() bool {
	return i == Unsupported
}
<|endoftext|>


<commit_before>package config

import (
	"context"
	"fmt"

	"github.com/werf/werf/pkg/giterminism_inspector"
)

type StapelImageBase struct {
	Name             string
	From             string
	FromLatest       bool
	FromImageName    string
	FromArtifactName string
	FromCacheVersion string
	Git              *GitManager
	Shell            *Shell
	Ansible          *Ansible
	Mount            []*Mount
	Import           []*Import

	raw *rawStapelImage
}

func (c *StapelImageBase) GetName() string {
	return c.Name
}

func (c *StapelImageBase) imports() []*Import {
	return c.Import
}

func (c *StapelImageBase) ImageBaseConfig() *StapelImageBase {
	return c
}

func (c *StapelImageBase) IsArtifact() bool {
	return false
}

func (c *StapelImageBase) exportsAutoExcluding() error {
	for _, exp1 := range c.exports() {
		for _, exp2 := range c.exports() {
			if exp1 == exp2 {
				continue
			}

			if !exp1.AutoExcludeExportAndCheck(exp2) {
				errMsg := fmt.Sprintf("Conflict between imports!\n\n%s\n%s", dumpConfigSection(exp1.GetRaw()), dumpConfigSection(exp2.GetRaw()))
				return newDetailedConfigError(errMsg, nil, c.raw.doc)
			}
		}
	}

	return nil
}

func (c *StapelImageBase) exports() []autoExcludeExport {
	var exports []autoExcludeExport
	if c.Git != nil {
		for _, git := range c.Git.Local {
			exports = append(exports, git)
		}

		for _, git := range c.Git.Remote {
			exports = append(exports, git)
		}
	}

	for _, imp := range c.Import {
		exports = append(exports, imp)
	}

	return exports
}

func (c *StapelImageBase) validate() error {
	if c.FromLatest && !giterminism_inspector.LooseGiterminism {
		if err := giterminism_inspector.ReportConfigStapelFromLatest(context.Background()); err != nil {
			errMsg := "\n\n" + err.Error()
			return newDetailedConfigError(errMsg, nil, c.raw.doc)
		}
	}

	if c.From == "" && c.raw.FromImage == "" && c.raw.FromArtifact == "" && c.FromImageName == "" && c.FromArtifactName == "" {
		return newDetailedConfigError("`from: DOCKER_IMAGE`, `fromImage: IMAGE_NAME`, `fromArtifact: IMAGE_ARTIFACT_NAME` required!", nil, c.raw.doc)
	}

	mountByTo := map[string]bool{}
	for _, mount := range c.Mount {
		_, exist := mountByTo[mount.To]
		if exist {
			return newDetailedConfigError("conflict between mounts!", nil, c.raw.doc)
		}

		mountByTo[mount.To] = true
	}

	if !oneOrNone([]bool{c.From != "", c.raw.FromImage != "", c.raw.FromArtifact != ""}) {
		return newDetailedConfigError("conflict between `from`, `fromImage` and `fromArtifact` directives!", nil, c.raw.doc)
	}

	// TODO: валидацию формата `From`
	// TODO: валидация формата `Name`

	return nil
}
<commit_msg>[config] Deprecate the fromArtifact directive<commit_after>package config

import (
	"context"
	"fmt"

	"github.com/werf/logboek"

	"github.com/werf/werf/pkg/giterminism_inspector"
)

type StapelImageBase struct {
	Name             string
	From             string
	FromLatest       bool
	FromImageName    string
	FromArtifactName string
	FromCacheVersion string
	Git              *GitManager
	Shell            *Shell
	Ansible          *Ansible
	Mount            []*Mount
	Import           []*Import

	raw *rawStapelImage
}

func (c *StapelImageBase) GetName() string {
	return c.Name
}

func (c *StapelImageBase) imports() []*Import {
	return c.Import
}

func (c *StapelImageBase) ImageBaseConfig() *StapelImageBase {
	return c
}

func (c *StapelImageBase) IsArtifact() bool {
	return false
}

func (c *StapelImageBase) exportsAutoExcluding() error {
	for _, exp1 := range c.exports() {
		for _, exp2 := range c.exports() {
			if exp1 == exp2 {
				continue
			}

			if !exp1.AutoExcludeExportAndCheck(exp2) {
				errMsg := fmt.Sprintf("Conflict between imports!\n\n%s\n%s", dumpConfigSection(exp1.GetRaw()), dumpConfigSection(exp2.GetRaw()))
				return newDetailedConfigError(errMsg, nil, c.raw.doc)
			}
		}
	}

	return nil
}

func (c *StapelImageBase) exports() []autoExcludeExport {
	var exports []autoExcludeExport
	if c.Git != nil {
		for _, git := range c.Git.Local {
			exports = append(exports, git)
		}

		for _, git := range c.Git.Remote {
			exports = append(exports, git)
		}
	}

	for _, imp := range c.Import {
		exports = append(exports, imp)
	}

	return exports
}

func (c *StapelImageBase) validate() error {
	if c.FromLatest && !giterminism_inspector.LooseGiterminism {
		if err := giterminism_inspector.ReportConfigStapelFromLatest(context.Background()); err != nil {
			errMsg := "\n\n" + err.Error()
			return newDetailedConfigError(errMsg, nil, c.raw.doc)
		}
	}

	if c.From == "" && c.raw.FromImage == "" && c.raw.FromArtifact == "" && c.FromImageName == "" && c.FromArtifactName == "" {
		return newDetailedConfigError("`from: DOCKER_IMAGE`, `fromImage: IMAGE_NAME`, `fromArtifact: IMAGE_ARTIFACT_NAME` required!", nil, c.raw.doc)
	}

	mountByTo := map[string]bool{}
	for _, mount := range c.Mount {
		_, exist := mountByTo[mount.To]
		if exist {
			return newDetailedConfigError("conflict between mounts!", nil, c.raw.doc)
		}

		mountByTo[mount.To] = true
	}

	if !oneOrNone([]bool{c.From != "", c.raw.FromImage != "", c.raw.FromArtifact != ""}) {
		return newDetailedConfigError("conflict between `from`, `fromImage` and `fromArtifact` directives!", nil, c.raw.doc)
	}

	if c.raw.FromArtifact != "" {
		logboek.Context(context.Background()).Warn().LogLn("DEPRECATION WARNING: The directive `fromArtifact` will be removed in v1.3. Use `fromImage` or `import` directive instead!")
	}

	// TODO: валидацию формата `From`

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    irc "github.com/fluffle/goirc/client"
    "sync"
)

var meebcast = meebCast{status: false}

type meebCast struct {
    status bool
    mutex sync.RWMutex
}

func meeba(channel, nick, command string, conn *irc.Conn) {
    if nick == "meeba" || nick == "sadbox" {
        meebcast.mutex.Lock()
        if command == "on" {
            meebcast.status = true
        } else if command == "off" {
            meebcast.status = false
        }
        meebcast.mutex.Unlock()
    }
    meebcast.mutex.RLock()
    defer meebcast.mutex.RUnlock()
    if meebcast.status {
        go conn.Privmsg(channel, "Drinking Problem show is \u00030,3on air\u0003! Tune in: http://radio.abstractionpoint.org")
    } else {
        go conn.Privmsg(channel, "Drinking Problem show is \u00030,4off the air\u0003! Tune in: http://radio.abstractionpoint.org")
    }
}
<commit_msg>only luck the write mutex if I need to write<commit_after>package main

import (
    irc "github.com/fluffle/goirc/client"
    "sync"
)

var meebcast = meebCast{status: false}

type meebCast struct {
    status bool
    mutex sync.RWMutex
}

func meeba(channel, nick, command string, conn *irc.Conn) {
    if nick == "meeba" || nick == "sadbox" {
        if command == "on" {
            meebcast.mutex.Lock()
            meebcast.status = true
            meebcast.mutex.Unlock()
        } else if command == "off" {
            meebcast.mutex.Lock()
            meebcast.status = false
            meebcast.mutex.Unlock()
        }
    }
    meebcast.mutex.RLock()
    defer meebcast.mutex.RUnlock()
    if meebcast.status {
        go conn.Privmsg(channel, "Drinking Problem show is \u00030,3on air\u0003! Tune in: http://radio.abstractionpoint.org")
    } else {
        go conn.Privmsg(channel, "Drinking Problem show is \u00030,4off the air\u0003! Tune in: http://radio.abstractionpoint.org")
    }
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/network"
	"github.com/dotcloud/docker/pkg/libcontainer/utils"
	"github.com/dotcloud/docker/pkg/system"
	"github.com/dotcloud/docker/pkg/term"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"
)

func execCommand(container *libcontainer.Container, args []string) (int, error) {
	master, console, err := createMasterAndConsole()
	if err != nil {
		return -1, err
	}

	// create a pipe so that we can syncronize with the namespaced process and
	// pass the veth name to the child
	r, w, err := os.Pipe()
	if err != nil {
		return -1, err
	}
	system.UsetCloseOnExec(r.Fd())

	command := createCommand(container, console, r.Fd(), args)

	if err := command.Start(); err != nil {
		return -1, err
	}

	if err := writePidFile(command); err != nil {
		command.Process.Kill()
		return -1, err
	}
	defer deletePidFile()

	// Do this before syncing with child so that no children
	// can escape the cgroup
	if container.Cgroups != nil {
		if err := container.Cgroups.Apply(command.Process.Pid); err != nil {
			command.Process.Kill()
			return -1, err
		}
	}

	if container.Network != nil {
		vethPair, err := initializeContainerVeth(container.Network.Bridge, command.Process.Pid)
		if err != nil {
			return -1, err
		}
		sendVethName(vethPair, w)
	}

	// Sync with child
	w.Close()

	go io.Copy(os.Stdout, master)
	go io.Copy(master, os.Stdin)

	state, err := setupWindow(master)
	if err != nil {
		command.Process.Kill()
		return -1, err
	}
	defer term.RestoreTerminal(os.Stdin.Fd(), state)

	if err := command.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}
	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}

// sendVethName writes the veth pair name to the child's stdin then closes the
// pipe so that the child stops waiting for more data
func sendVethName(name string, pipe io.WriteCloser) {
	fmt.Fprint(pipe, name)
}

// initializeContainerVeth will create a veth pair and setup the host's
// side of the pair by setting the specified bridge as the master and bringing
// up the interface.
//
// Then will with set the other side of the veth pair into the container's namespaced
// using the pid and returns the veth's interface name to provide to the container to
// finish setting up the interface inside the namespace
func initializeContainerVeth(bridge string, nspid int) (string, error) {
	name1, name2, err := createVethPair()
	if err != nil {
		return "", err
	}
	if err := network.SetInterfaceMaster(name1, bridge); err != nil {
		return "", err
	}
	if err := network.InterfaceUp(name1); err != nil {
		return "", err
	}
	if err := network.SetInterfaceInNamespacePid(name2, nspid); err != nil {
		return "", err
	}
	return name2, nil
}

func setupWindow(master *os.File) (*term.State, error) {
	ws, err := term.GetWinsize(os.Stdin.Fd())
	if err != nil {
		return nil, err
	}
	if err := term.SetWinsize(master.Fd(), ws); err != nil {
		return nil, err
	}
	return term.SetRawTerminal(os.Stdin.Fd())
}

// createMasterAndConsole will open /dev/ptmx on the host and retreive the
// pts name for use as the pty slave inside the container
func createMasterAndConsole() (*os.File, string, error) {
	master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
	if err != nil {
		return nil, "", err
	}
	console, err := system.Ptsname(master)
	if err != nil {
		return nil, "", err
	}
	if err := system.Unlockpt(master); err != nil {
		return nil, "", err
	}
	return master, console, nil
}

// createVethPair will automatically generage two random names for
// the veth pair and ensure that they have been created
func createVethPair() (name1 string, name2 string, err error) {
	name1, err = utils.GenerateRandomName("dock", 4)
	if err != nil {
		return
	}
	name2, err = utils.GenerateRandomName("dock", 4)
	if err != nil {
		return
	}
	if err = network.CreateVethPair(name1, name2); err != nil {
		return
	}
	return
}

// writePidFile writes the namespaced processes pid to .nspid in the rootfs for the container
func writePidFile(command *exec.Cmd) error {
	return ioutil.WriteFile(".nspid", []byte(fmt.Sprint(command.Process.Pid)), 0655)
}

func deletePidFile() error {
	return os.Remove(".nspid")
}

// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
// defined on the container's configuration and use the current binary as the init with the
// args provided
func createCommand(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd {
	command := exec.Command("nsinit", append([]string{"-console", console, "-pipe", fmt.Sprint(pipe), "init"}, args...)...)
	command.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: uintptr(getNamespaceFlags(container.Namespaces)),
	}
	return command
}
<commit_msg>Minor cleanup<commit_after>// +build linux

package main

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/network"
	"github.com/dotcloud/docker/pkg/libcontainer/utils"
	"github.com/dotcloud/docker/pkg/system"
	"github.com/dotcloud/docker/pkg/term"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"
)

func execCommand(container *libcontainer.Container, args []string) (int, error) {
	master, console, err := createMasterAndConsole()
	if err != nil {
		return -1, err
	}

	// create a pipe so that we can syncronize with the namespaced process and
	// pass the veth name to the child
	r, w, err := os.Pipe()
	if err != nil {
		return -1, err
	}
	system.UsetCloseOnExec(r.Fd())

	command := createCommand(container, console, r.Fd(), args)

	if err := command.Start(); err != nil {
		return -1, err
	}

	if err := writePidFile(command); err != nil {
		command.Process.Kill()
		return -1, err
	}
	defer deletePidFile()

	// Do this before syncing with child so that no children
	// can escape the cgroup
	if container.Cgroups != nil {
		if err := container.Cgroups.Apply(command.Process.Pid); err != nil {
			command.Process.Kill()
			return -1, err
		}
	}

	if container.Network != nil {
		vethPair, err := initializeContainerVeth(container.Network.Bridge, command.Process.Pid)
		if err != nil {
			return -1, err
		}
		sendVethName(w, vethPair)
	}

	// Sync with child
	w.Close()
	r.Close()

	go io.Copy(os.Stdout, master)
	go io.Copy(master, os.Stdin)

	state, err := setupWindow(master)
	if err != nil {
		command.Process.Kill()
		return -1, err
	}
	defer term.RestoreTerminal(os.Stdin.Fd(), state)

	if err := command.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}
	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}

// sendVethName writes the veth pair name to the child's stdin then closes the
// pipe so that the child stops waiting for more data
func sendVethName(pipe io.Writer, name string) {
	fmt.Fprint(pipe, name)
}

// initializeContainerVeth will create a veth pair and setup the host's
// side of the pair by setting the specified bridge as the master and bringing
// up the interface.
//
// Then will with set the other side of the veth pair into the container's namespaced
// using the pid and returns the veth's interface name to provide to the container to
// finish setting up the interface inside the namespace
func initializeContainerVeth(bridge string, nspid int) (string, error) {
	name1, name2, err := createVethPair()
	if err != nil {
		return "", err
	}
	if err := network.SetInterfaceMaster(name1, bridge); err != nil {
		return "", err
	}
	if err := network.InterfaceUp(name1); err != nil {
		return "", err
	}
	if err := network.SetInterfaceInNamespacePid(name2, nspid); err != nil {
		return "", err
	}
	return name2, nil
}

func setupWindow(master *os.File) (*term.State, error) {
	ws, err := term.GetWinsize(os.Stdin.Fd())
	if err != nil {
		return nil, err
	}
	if err := term.SetWinsize(master.Fd(), ws); err != nil {
		return nil, err
	}
	return term.SetRawTerminal(os.Stdin.Fd())
}

// createMasterAndConsole will open /dev/ptmx on the host and retreive the
// pts name for use as the pty slave inside the container
func createMasterAndConsole() (*os.File, string, error) {
	master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
	if err != nil {
		return nil, "", err
	}
	console, err := system.Ptsname(master)
	if err != nil {
		return nil, "", err
	}
	if err := system.Unlockpt(master); err != nil {
		return nil, "", err
	}
	return master, console, nil
}

// createVethPair will automatically generage two random names for
// the veth pair and ensure that they have been created
func createVethPair() (name1 string, name2 string, err error) {
	name1, err = utils.GenerateRandomName("dock", 4)
	if err != nil {
		return
	}
	name2, err = utils.GenerateRandomName("dock", 4)
	if err != nil {
		return
	}
	if err = network.CreateVethPair(name1, name2); err != nil {
		return
	}
	return
}

// writePidFile writes the namespaced processes pid to .nspid in the rootfs for the container
func writePidFile(command *exec.Cmd) error {
	return ioutil.WriteFile(".nspid", []byte(fmt.Sprint(command.Process.Pid)), 0655)
}

func deletePidFile() error {
	return os.Remove(".nspid")
}

// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
// defined on the container's configuration and use the current binary as the init with the
// args provided
func createCommand(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd {
	command := exec.Command("nsinit", append([]string{"-console", console, "-pipe", fmt.Sprint(pipe), "init"}, args...)...)
	command.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: uintptr(getNamespaceFlags(container.Namespaces)),
	}
	return command
}
<|endoftext|>


<commit_before>package janus

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

type transport struct {
	http.RoundTripper
	statsdClient *statsd.Client
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	timing := t.statsdClient.NewTiming()
	resp, err = t.RoundTripper.RoundTrip(req)
	timing.Send(getStatsdMetricName(req))

	if resp.StatusCode >= 400 {
		t.statsdClient.Increment("error_request")
	} else if resp.StatusCode < 300 && resp.Body != nil {
		t.statsdClient.Increment("success_request")

		//This is useful for the middlewares
		var bodyBytes []byte

		defer resp.Body.Close()
		bodyBytes, _ = ioutil.ReadAll(resp.Body)

		// Restore the io.ReadCloser to its original state
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		// Use the content
		log.WithFields(log.Fields{
			"req":  req,
			"resp": resp,
		}).Info("Setting body")

		t.context.Set("body", bodyBytes)
	} else {
		t.statsdClient.Increment("success_request")
	}

	return resp, err
}

// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		path := target.Path

		if proxy.StripListenPath {
			path = singleJoiningSlash(target.Path, req.URL.Path)

			listenPath := extractListenPath(proxy.ListenPath)
			log.Debug("Stripping: ", listenPath)
			path = strings.Replace(path, listenPath, "", 1)

			log.Debug("Upstream Path is: ", path)

			if !strings.HasSuffix(target.Path, "/") && strings.HasSuffix(path, "/") {
				path = path[:len(path)-1]
			}
		}

		req.URL.Path = path

		// This is very important to avoid problems with ssl verification for the HOST header
		if !proxy.PreserveHostHeader {
			req.Host = target.Host
		}
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

func extractListenPath(listenPath string) string {
	var reg = regexp.MustCompile(`(\/\*.+)`)
	return reg.ReplaceAllString(listenPath, "")
}

func cleanSlashes(a string) string {
	endSlash := strings.HasSuffix(a, "//")
	startSlash := strings.HasPrefix(a, "//")

	if startSlash {
		a = "/" + strings.TrimPrefix(a, "//")
	}

	if endSlash {
		a = strings.TrimSuffix(a, "//") + "/"
	}

	return a
}

func singleJoiningSlash(a, b string) string {
	a = cleanSlashes(a)
	b = cleanSlashes(b)

	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	switch {
	case aslash && bslash:
		log.Debug(a + b)
		return a + b[1:]
	case !aslash && !bslash:
		if len(b) > 0 {
			log.Debug(a + b)
			return a + "/" + b
		}

		log.Debug(a + b)
		return a
	}

	log.Debug(a + b)
	return a + b
}

// Returns metric name for StatsD in "<request method>.<request path>" format
func getStatsdMetricName(req *http.Request) string {
	return fmt.Sprintf(
		"%s.%s",
		strings.ToLower(req.Method),
		strings.Replace(
			// Double underscores
			strings.Replace(req.URL.Path, "_", "__", -1),
			// and replace dots with single underscore
			".",
			"_",
			-1))
}
<commit_msg>Fixed context error<commit_after>package janus

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"

	"context"

	log "github.com/Sirupsen/logrus"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

type transport struct {
	http.RoundTripper
	statsdClient *statsd.Client
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	timing := t.statsdClient.NewTiming()
	resp, err = t.RoundTripper.RoundTrip(req)
	timing.Send(getStatsdMetricName(req))

	if resp.StatusCode >= 400 {
		t.statsdClient.Increment("error_request")
	} else if resp.StatusCode < 300 && resp.Body != nil {
		t.statsdClient.Increment("success_request")

		//This is useful for the middlewares
		var bodyBytes []byte

		defer resp.Body.Close()
		bodyBytes, _ = ioutil.ReadAll(resp.Body)

		// Restore the io.ReadCloser to its original state
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		// Use the content
		log.WithFields(log.Fields{
			"req":  req,
			"resp": resp,
		}).Info("Setting body")

		context.WithValue(req.Context(), "body", bodyBytes)
	} else {
		t.statsdClient.Increment("success_request")
	}

	return resp, err
}

// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		path := target.Path

		if proxy.StripListenPath {
			path = singleJoiningSlash(target.Path, req.URL.Path)

			listenPath := extractListenPath(proxy.ListenPath)
			log.Debug("Stripping: ", listenPath)
			path = strings.Replace(path, listenPath, "", 1)

			log.Debug("Upstream Path is: ", path)

			if !strings.HasSuffix(target.Path, "/") && strings.HasSuffix(path, "/") {
				path = path[:len(path)-1]
			}
		}

		req.URL.Path = path

		// This is very important to avoid problems with ssl verification for the HOST header
		if !proxy.PreserveHostHeader {
			req.Host = target.Host
		}
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

func extractListenPath(listenPath string) string {
	var reg = regexp.MustCompile(`(\/\*.+)`)
	return reg.ReplaceAllString(listenPath, "")
}

func cleanSlashes(a string) string {
	endSlash := strings.HasSuffix(a, "//")
	startSlash := strings.HasPrefix(a, "//")

	if startSlash {
		a = "/" + strings.TrimPrefix(a, "//")
	}

	if endSlash {
		a = strings.TrimSuffix(a, "//") + "/"
	}

	return a
}

func singleJoiningSlash(a, b string) string {
	a = cleanSlashes(a)
	b = cleanSlashes(b)

	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	switch {
	case aslash && bslash:
		log.Debug(a + b)
		return a + b[1:]
	case !aslash && !bslash:
		if len(b) > 0 {
			log.Debug(a + b)
			return a + "/" + b
		}

		log.Debug(a + b)
		return a
	}

	log.Debug(a + b)
	return a + b
}

// Returns metric name for StatsD in "<request method>.<request path>" format
func getStatsdMetricName(req *http.Request) string {
	return fmt.Sprintf(
		"%s.%s",
		strings.ToLower(req.Method),
		strings.Replace(
			// Double underscores
			strings.Replace(req.URL.Path, "_", "__", -1),
			// and replace dots with single underscore
			".",
			"_",
			-1))
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes 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 namespace

import (
	"time"

	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
	"k8s.io/kubernetes/pkg/client/typed/dynamic"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/schema"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/pkg/util/workqueue"
	"k8s.io/kubernetes/pkg/watch"

	"github.com/golang/glog"
)

// NamespaceController is responsible for performing actions dependent upon a namespace phase
type NamespaceController struct {
	// client that purges namespace content, must have list/delete privileges on all content
	kubeClient clientset.Interface
	// clientPool manages a pool of dynamic clients
	clientPool dynamic.ClientPool
	// store that holds the namespaces
	store cache.Store
	// controller that observes the namespaces
	controller *cache.Controller
	// namespaces that have been queued up for processing by workers
	queue workqueue.RateLimitingInterface
	// function to list of preferred group versions and their corresponding resource set for namespace deletion
	groupVersionResourcesFn func() ([]schema.GroupVersionResource, error)
	// opCache is a cache to remember if a particular operation is not supported to aid dynamic client.
	opCache *operationNotSupportedCache
	// finalizerToken is the finalizer token managed by this controller
	finalizerToken v1.FinalizerName
}

// NewNamespaceController creates a new NamespaceController
func NewNamespaceController(
	kubeClient clientset.Interface,
	clientPool dynamic.ClientPool,
	groupVersionResourcesFn func() ([]schema.GroupVersionResource, error),
	resyncPeriod time.Duration,
	finalizerToken v1.FinalizerName) *NamespaceController {

	// the namespace deletion code looks at the discovery document to enumerate the set of resources on the server.
	// it then finds all namespaced resources, and in response to namespace deletion, will call delete on all of them.
	// unfortunately, the discovery information does not include the list of supported verbs/methods.  if the namespace
	// controller calls LIST/DELETECOLLECTION for a resource, it will get a 405 error from the server and cache that that was the case.
	// we found in practice though that some auth engines when encountering paths they don't know about may return a 50x.
	// until we have verbs, we pre-populate resources that do not support list or delete for well-known apis rather than
	// probing the server once in order to be told no.
	opCache := &operationNotSupportedCache{
		m: make(map[operationKey]bool),
	}
	ignoredGroupVersionResources := []schema.GroupVersionResource{
		{Group: "", Version: "v1", Resource: "bindings"},
	}
	for _, ignoredGroupVersionResource := range ignoredGroupVersionResources {
		opCache.setNotSupported(operationKey{op: operationDeleteCollection, gvr: ignoredGroupVersionResource})
		opCache.setNotSupported(operationKey{op: operationList, gvr: ignoredGroupVersionResource})
	}

	// create the controller so we can inject the enqueue function
	namespaceController := &NamespaceController{
		kubeClient: kubeClient,
		clientPool: clientPool,
		queue:      workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
		groupVersionResourcesFn: groupVersionResourcesFn,
		opCache:                 opCache,
		finalizerToken:          finalizerToken,
	}

	if kubeClient != nil && kubeClient.Core().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("namespace_controller", kubeClient.Core().RESTClient().GetRateLimiter())
	}

	// configure the backing store/controller
	store, controller := cache.NewInformer(
		&cache.ListWatch{
			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
				return kubeClient.Core().Namespaces().List(options)
			},
			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
				return kubeClient.Core().Namespaces().Watch(options)
			},
		},
		&v1.Namespace{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				namespace := obj.(*v1.Namespace)
				namespaceController.enqueueNamespace(namespace)
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				namespace := newObj.(*v1.Namespace)
				namespaceController.enqueueNamespace(namespace)
			},
		},
	)

	namespaceController.store = store
	namespaceController.controller = controller
	return namespaceController
}

// enqueueNamespace adds an object to the controller work queue
// obj could be an *v1.Namespace, or a DeletionFinalStateUnknown item.
func (nm *NamespaceController) enqueueNamespace(obj interface{}) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
		return
	}
	nm.queue.Add(key)
}

// worker processes the queue of namespace objects.
// Each namespace can be in the queue at most once.
// The system ensures that no two workers can process
// the same namespace at the same time.
func (nm *NamespaceController) worker() {
	workFunc := func() bool {
		key, quit := nm.queue.Get()
		if quit {
			return true
		}
		defer nm.queue.Done(key)

		err := nm.syncNamespaceFromKey(key.(string))
		if err == nil {
			// no error, forget this entry and return
			nm.queue.Forget(key)
			return false
		}

		if estimate, ok := err.(*contentRemainingError); ok {
			t := estimate.Estimate/2 + 1
			glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", key, t)
			nm.queue.AddAfter(key, time.Duration(t)*time.Second)
		} else {
			// rather than wait for a full resync, re-add the namespace to the queue to be processed
			nm.queue.AddRateLimited(key)
			utilruntime.HandleError(err)
		}
		return false
	}

	for {
		quit := workFunc()

		if quit {
			return
		}
	}
}

// syncNamespaceFromKey looks for a namespace with the specified key in its store and synchronizes it
func (nm *NamespaceController) syncNamespaceFromKey(key string) (err error) {
	startTime := time.Now()
	defer glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Now().Sub(startTime))

	obj, exists, err := nm.store.GetByKey(key)
	if !exists {
		glog.Infof("Namespace has been deleted %v", key)
		return nil
	}
	if err != nil {
		glog.Errorf("Unable to retrieve namespace %v from store: %v", key, err)
		nm.queue.Add(key)
		return err
	}
	namespace := obj.(*v1.Namespace)
	return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResourcesFn, namespace, nm.finalizerToken)
}

// Run starts observing the system with the specified number of workers.
func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	go nm.controller.Run(stopCh)
	for i := 0; i < workers; i++ {
		go wait.Until(nm.worker, time.Second, stopCh)
	}
	<-stopCh
	glog.Infof("Shutting down NamespaceController")
	nm.queue.ShutDown()
}
<commit_msg>hold namespaces briefly before processing deletion<commit_after>/*
Copyright 2015 The Kubernetes 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 namespace

import (
	"time"

	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
	"k8s.io/kubernetes/pkg/client/typed/dynamic"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/schema"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/pkg/util/workqueue"
	"k8s.io/kubernetes/pkg/watch"

	"github.com/golang/glog"
)

const (
	// namespaceDeletionGracePeriod is the time period to wait before processing a received namespace event.
	// This allows time for the following to occur:
	// * lifecycle admission plugins on HA apiservers to also observe a namespace
	//   deletion and prevent new objects from being created in the terminating namespace
	// * non-leader etcd servers to observe last-minute object creations in a namespace
	//   so this controller's cleanup can actually clean up all objects
	namespaceDeletionGracePeriod = 5 * time.Second
)

// NamespaceController is responsible for performing actions dependent upon a namespace phase
type NamespaceController struct {
	// client that purges namespace content, must have list/delete privileges on all content
	kubeClient clientset.Interface
	// clientPool manages a pool of dynamic clients
	clientPool dynamic.ClientPool
	// store that holds the namespaces
	store cache.Store
	// controller that observes the namespaces
	controller *cache.Controller
	// namespaces that have been queued up for processing by workers
	queue workqueue.RateLimitingInterface
	// function to list of preferred group versions and their corresponding resource set for namespace deletion
	groupVersionResourcesFn func() ([]schema.GroupVersionResource, error)
	// opCache is a cache to remember if a particular operation is not supported to aid dynamic client.
	opCache *operationNotSupportedCache
	// finalizerToken is the finalizer token managed by this controller
	finalizerToken v1.FinalizerName
}

// NewNamespaceController creates a new NamespaceController
func NewNamespaceController(
	kubeClient clientset.Interface,
	clientPool dynamic.ClientPool,
	groupVersionResourcesFn func() ([]schema.GroupVersionResource, error),
	resyncPeriod time.Duration,
	finalizerToken v1.FinalizerName) *NamespaceController {

	// the namespace deletion code looks at the discovery document to enumerate the set of resources on the server.
	// it then finds all namespaced resources, and in response to namespace deletion, will call delete on all of them.
	// unfortunately, the discovery information does not include the list of supported verbs/methods.  if the namespace
	// controller calls LIST/DELETECOLLECTION for a resource, it will get a 405 error from the server and cache that that was the case.
	// we found in practice though that some auth engines when encountering paths they don't know about may return a 50x.
	// until we have verbs, we pre-populate resources that do not support list or delete for well-known apis rather than
	// probing the server once in order to be told no.
	opCache := &operationNotSupportedCache{
		m: make(map[operationKey]bool),
	}
	ignoredGroupVersionResources := []schema.GroupVersionResource{
		{Group: "", Version: "v1", Resource: "bindings"},
	}
	for _, ignoredGroupVersionResource := range ignoredGroupVersionResources {
		opCache.setNotSupported(operationKey{op: operationDeleteCollection, gvr: ignoredGroupVersionResource})
		opCache.setNotSupported(operationKey{op: operationList, gvr: ignoredGroupVersionResource})
	}

	// create the controller so we can inject the enqueue function
	namespaceController := &NamespaceController{
		kubeClient: kubeClient,
		clientPool: clientPool,
		queue:      workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
		groupVersionResourcesFn: groupVersionResourcesFn,
		opCache:                 opCache,
		finalizerToken:          finalizerToken,
	}

	if kubeClient != nil && kubeClient.Core().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("namespace_controller", kubeClient.Core().RESTClient().GetRateLimiter())
	}

	// configure the backing store/controller
	store, controller := cache.NewInformer(
		&cache.ListWatch{
			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
				return kubeClient.Core().Namespaces().List(options)
			},
			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
				return kubeClient.Core().Namespaces().Watch(options)
			},
		},
		&v1.Namespace{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				namespace := obj.(*v1.Namespace)
				namespaceController.enqueueNamespace(namespace)
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				namespace := newObj.(*v1.Namespace)
				namespaceController.enqueueNamespace(namespace)
			},
		},
	)

	namespaceController.store = store
	namespaceController.controller = controller
	return namespaceController
}

// enqueueNamespace adds an object to the controller work queue
// obj could be an *v1.Namespace, or a DeletionFinalStateUnknown item.
func (nm *NamespaceController) enqueueNamespace(obj interface{}) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
		return
	}
	// delay processing namespace events to allow HA api servers to observe namespace deletion,
	// and HA etcd servers to observe last minute object creations inside the namespace
	nm.queue.AddAfter(key, namespaceDeletionGracePeriod)
}

// worker processes the queue of namespace objects.
// Each namespace can be in the queue at most once.
// The system ensures that no two workers can process
// the same namespace at the same time.
func (nm *NamespaceController) worker() {
	workFunc := func() bool {
		key, quit := nm.queue.Get()
		if quit {
			return true
		}
		defer nm.queue.Done(key)

		err := nm.syncNamespaceFromKey(key.(string))
		if err == nil {
			// no error, forget this entry and return
			nm.queue.Forget(key)
			return false
		}

		if estimate, ok := err.(*contentRemainingError); ok {
			t := estimate.Estimate/2 + 1
			glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", key, t)
			nm.queue.AddAfter(key, time.Duration(t)*time.Second)
		} else {
			// rather than wait for a full resync, re-add the namespace to the queue to be processed
			nm.queue.AddRateLimited(key)
			utilruntime.HandleError(err)
		}
		return false
	}

	for {
		quit := workFunc()

		if quit {
			return
		}
	}
}

// syncNamespaceFromKey looks for a namespace with the specified key in its store and synchronizes it
func (nm *NamespaceController) syncNamespaceFromKey(key string) (err error) {
	startTime := time.Now()
	defer glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Now().Sub(startTime))

	obj, exists, err := nm.store.GetByKey(key)
	if !exists {
		glog.Infof("Namespace has been deleted %v", key)
		return nil
	}
	if err != nil {
		glog.Errorf("Unable to retrieve namespace %v from store: %v", key, err)
		nm.queue.Add(key)
		return err
	}
	namespace := obj.(*v1.Namespace)
	return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResourcesFn, namespace, nm.finalizerToken)
}

// Run starts observing the system with the specified number of workers.
func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	go nm.controller.Run(stopCh)
	for i := 0; i < workers; i++ {
		go wait.Until(nm.worker, time.Second, stopCh)
	}
	<-stopCh
	glog.Infof("Shutting down NamespaceController")
	nm.queue.ShutDown()
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package assert

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"reflect"
	"strings"
	"testing"
)

// Assertion 可以以对象的方式调用包中的各个断言函数
type Assertion struct {
	tb testing.TB

	fatal bool
	print func(...interface{})
	f     FailureSprintFunc
}

// New 返回 Assertion 对象
//
// fatal 决定在出错时是调用 tb.Error 还是 tb.Fatal；
func New(tb testing.TB, fatal bool) *Assertion {
	p := tb.Error
	if fatal {
		p = tb.Fatal
	}

	return &Assertion{
		tb: tb,

		fatal: fatal,
		print: p,
		f:     failureSprint,
	}
}

// NewWithEnv 以指定的环境变量初始化 *Assertion 对象
//
// env 是以 t.Setenv 的形式调用。
func NewWithEnv(tb testing.TB, fatal bool, env map[string]string) *Assertion {
	for k, v := range env {
		tb.Setenv(k, v)
	}
	return New(tb, fatal)
}

// Assert 断言 expr 条件成立
//
// f 表示在断言失败时输出的信息
//
// 普通用户直接使用 True 效果是一样的，此函数主要供库调用。
func (a *Assertion) Assert(expr bool, f *Failure) *Assertion {
	if !expr {
		a.TB().Helper()
		a.print(a.f(f))
	}
	return a
}

// TB 返回 [testing.TB] 接口
func (a *Assertion) TB() testing.TB { return a.tb }

// True 断言表达式 expr 为真
//
// args 对应 fmt.Printf() 函数中的参数，其中 args[0] 对应第一个参数 format，依次类推，
// 具体可参数 Assert 方法的介绍。其它断言函数的 args 参数，功能与此相同。
func (a *Assertion) True(expr bool, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(expr, NewFailure("True", msg, nil))
}

func (a *Assertion) False(expr bool, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!expr, NewFailure("False", msg, nil))
}

func (a *Assertion) Nil(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isNil(expr), NewFailure("Nil", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) NotNil(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isNil(expr), NewFailure("NotNil", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) Equal(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isEqual(v1, v2), NewFailure("Equal", msg, map[string]interface{}{"v1": v1, "v2": v2}))
}

func (a *Assertion) NotEqual(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isEqual(v1, v2), NewFailure("NotEqual", msg, map[string]interface{}{"v1": v1, "v2": v2}))
}

func (a *Assertion) Empty(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isEmpty(expr), NewFailure("Empty", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) NotEmpty(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isEmpty(expr), NewFailure("NotEmpty", msg, map[string]interface{}{"v": expr}))
}

// Error 断言有错误发生
//
// 传递未初始化的 error 值(var err error = nil)，将断言失败
//
// NotNil 的特化版本，限定了类型为 error。
func (a *Assertion) Error(expr error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isNil(expr), NewFailure("Error", msg, map[string]interface{}{"v": expr}))
}

// ErrorString 断言有错误发生且错误信息中包含指定的字符串 str
//
// 传递未初始化的 error 值(var err error = nil)，将断言失败
func (a *Assertion) ErrorString(expr error, str string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if isNil(expr) { // 空值，必定没有错误
		return a.Assert(false, NewFailure("ErrorString", msg, map[string]interface{}{"v": expr}))
	}
	return a.Assert(strings.Contains(expr.Error(), str), NewFailure("ErrorString", msg, map[string]interface{}{"v": expr}))
}

// ErrorIs 断言 expr 为 target 类型
//
// 相当于 a.True(errors.Is(expr, target))
func (a *Assertion) ErrorIs(expr, target error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(errors.Is(expr, target), NewFailure("ErrorIs", msg, nil))
}

// NotError 断言没有错误
//
// Nil 的特化版本，限定了类型为 error。
func (a *Assertion) NotError(expr error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isNil(expr), NewFailure("NotError", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) FileExists(path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if _, err := os.Stat(path); err != nil && !errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileExists", msg, map[string]interface{}{"err": err}))
	}
	return a
}

func (a *Assertion) FileNotExists(path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	_, err := os.Stat(path)
	if err == nil {
		return a.Assert(false, NewFailure("FileNotExists", msg, nil))
	}
	if errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileNotExists", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) FileExistsFS(fsys fs.FS, path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if _, err := fs.Stat(fsys, path); err != nil && !errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileExistsFS", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) FileNotExistsFS(fsys fs.FS, path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	_, err := fs.Stat(fsys, path)
	if err == nil {
		return a.Assert(false, NewFailure("FileNotExistsFS", msg, nil))
	}
	if errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileNotExistsFS", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) Panic(fn func(), msg ...interface{}) *Assertion {
	a.TB().Helper()

	has, _ := hasPanic(fn)
	return a.Assert(has, NewFailure("Panic", msg, nil))
}

// PanicString 断言函数会发生 panic 且 panic 信息中包含指定的字符串内容
func (a *Assertion) PanicString(fn func(), str string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if has, m := hasPanic(fn); has {
		return a.Assert(strings.Contains(fmt.Sprint(m), str), NewFailure("PanicString", msg, map[string]interface{}{"msg": m}))
	}
	return a.Assert(false, NewFailure("PanicString", msg, nil))
}

// PanicType 断言函数会发生 panic 且抛出指定的类型
func (a *Assertion) PanicType(fn func(), typ interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if has, m := hasPanic(fn); has {
		t1, t2 := getType(true, m, typ)
		return a.Assert(t1 == t2, NewFailure("PanicType", msg, map[string]interface{}{"v1": t1, "v2": t2}))
	}
	return a.Assert(false, NewFailure("PanicType", msg, nil))
}

func (a *Assertion) NotPanic(fn func(), msg ...interface{}) *Assertion {
	a.TB().Helper()

	has, m := hasPanic(fn)
	return a.Assert(!has, NewFailure("NotPanic", msg, map[string]interface{}{"err": m}))
}

// Contains 断言 container 包含 item 或是包含 item 中的所有项
//
// 若 container 是字符串(string、[]byte 和 []rune，不包含 fmt.Stringer 接口)，
// 都将会以字符串的形式判断其是否包含 item。
// 若 container 是个列表(array、slice、map)则判断其元素中是否包含 item 中的
// 的所有项，或是 item 本身就是 container 中的一个元素。
func (a *Assertion) Contains(container, item interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isContains(container, item), NewFailure("Contains", msg, map[string]interface{}{"container": container, "item": item}))
}

// NotContains 断言 container 不包含 item 或是不包含 item 中的所有项
func (a *Assertion) NotContains(container, item interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isContains(container, item), NewFailure("NotContains", msg, map[string]interface{}{"container": container, "item": item}))
}

// Zero 断言是否为零值
//
// 最终调用的是 reflect.Value.IsZero 进行判断
func (a *Assertion) Zero(v interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	isZero := v == nil || reflect.ValueOf(v).IsZero()
	return a.Assert(isZero, NewFailure("Zero", msg, map[string]interface{}{"v": v}))
}

// NotZero 断言是否为非零值
//
// 最终调用的是 reflect.Value.IsZero 进行判断
func (a *Assertion) NotZero(v interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	isZero := v == nil || reflect.ValueOf(v).IsZero()
	return a.Assert(!isZero, NewFailure("NotZero", msg, map[string]interface{}{"v": v}))
}

// Length 断言长度是否为指定的值
//
// v 可以是以下类型：
//   - map
//   - string
//   - slice
//   - array
func (a *Assertion) Length(v interface{}, l int, msg ...interface{}) *Assertion {
	a.TB().Helper()

	rl, err := getLen(v)
	if err != "" {
		a.Assert(false, NewFailure("Length", msg, map[string]interface{}{"err": err}))
	}
	return a.Assert(rl == l, NewFailure("Length", msg, map[string]interface{}{"l1": rl, "l2": l}))
}

// NotLength 断言长度不是指定的值
//
// v 可以是以下类型：
//   - map
//   - string
//   - slice
//   - array
func (a *Assertion) NotLength(v interface{}, l int, msg ...interface{}) *Assertion {
	a.TB().Helper()

	rl, err := getLen(v)
	if err != "" {
		a.Assert(false, NewFailure("NotLength", msg, map[string]interface{}{"err": err}))
	}
	return a.Assert(rl != l, NewFailure("NotLength", msg, map[string]interface{}{"l": rl}))
}

// TypeEqual 断言两个值的类型是否相同
//
// ptr 如果为 true，则会在对象为指针时，查找其指向的对象。
func (a *Assertion) TypeEqual(ptr bool, v1, v2 interface{}, msg ...interface{}) *Assertion {
	if v1 == v2 {
		return a
	}

	a.TB().Helper()

	t1, t2 := getType(ptr, v1, v2)
	return a.Assert(t1 == t2, NewFailure("TypeEquaal", msg, map[string]interface{}{"v1": t1, "v2": t2}))
}

// Same 断言为同一个对象
func (a *Assertion) Same(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isSame(v1, v2), NewFailure("Same", msg, nil))
}

// NotSame 断言为不是同一个对象
func (a *Assertion) NotSame(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isSame(v1, v2), NewFailure("NotSame", msg, nil))
}

func isSame(v1, v2 interface{}) bool {
	rv1 := reflect.ValueOf(v1)
	if !canPointer(rv1.Kind()) {
		return false
	}
	rv2 := reflect.ValueOf(v2)
	if !canPointer(rv2.Kind()) {
		return false
	}

	return rv1.Pointer() == rv2.Pointer()
}

func canPointer(k reflect.Kind) bool {
	switch k {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice, reflect.UnsafePointer, reflect.Func:
		return true
	default:
		return false
	}
}
<commit_msg>fix: 为 Assertion.ErrorrIs 出错是返回错误内容<commit_after>// SPDX-License-Identifier: MIT

package assert

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"reflect"
	"strings"
	"testing"
)

// Assertion 可以以对象的方式调用包中的各个断言函数
type Assertion struct {
	tb testing.TB

	fatal bool
	print func(...interface{})
	f     FailureSprintFunc
}

// New 返回 Assertion 对象
//
// fatal 决定在出错时是调用 tb.Error 还是 tb.Fatal；
func New(tb testing.TB, fatal bool) *Assertion {
	p := tb.Error
	if fatal {
		p = tb.Fatal
	}

	return &Assertion{
		tb: tb,

		fatal: fatal,
		print: p,
		f:     failureSprint,
	}
}

// NewWithEnv 以指定的环境变量初始化 *Assertion 对象
//
// env 是以 t.Setenv 的形式调用。
func NewWithEnv(tb testing.TB, fatal bool, env map[string]string) *Assertion {
	for k, v := range env {
		tb.Setenv(k, v)
	}
	return New(tb, fatal)
}

// Assert 断言 expr 条件成立
//
// f 表示在断言失败时输出的信息
//
// 普通用户直接使用 True 效果是一样的，此函数主要供库调用。
func (a *Assertion) Assert(expr bool, f *Failure) *Assertion {
	if !expr {
		a.TB().Helper()
		a.print(a.f(f))
	}
	return a
}

// TB 返回 [testing.TB] 接口
func (a *Assertion) TB() testing.TB { return a.tb }

// True 断言表达式 expr 为真
//
// args 对应 fmt.Printf() 函数中的参数，其中 args[0] 对应第一个参数 format，依次类推，
// 具体可参数 Assert 方法的介绍。其它断言函数的 args 参数，功能与此相同。
func (a *Assertion) True(expr bool, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(expr, NewFailure("True", msg, nil))
}

func (a *Assertion) False(expr bool, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!expr, NewFailure("False", msg, nil))
}

func (a *Assertion) Nil(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isNil(expr), NewFailure("Nil", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) NotNil(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isNil(expr), NewFailure("NotNil", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) Equal(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isEqual(v1, v2), NewFailure("Equal", msg, map[string]interface{}{"v1": v1, "v2": v2}))
}

func (a *Assertion) NotEqual(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isEqual(v1, v2), NewFailure("NotEqual", msg, map[string]interface{}{"v1": v1, "v2": v2}))
}

func (a *Assertion) Empty(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isEmpty(expr), NewFailure("Empty", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) NotEmpty(expr interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isEmpty(expr), NewFailure("NotEmpty", msg, map[string]interface{}{"v": expr}))
}

// Error 断言有错误发生
//
// 传递未初始化的 error 值(var err error = nil)，将断言失败
//
// NotNil 的特化版本，限定了类型为 error。
func (a *Assertion) Error(expr error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isNil(expr), NewFailure("Error", msg, map[string]interface{}{"v": expr}))
}

// ErrorString 断言有错误发生且错误信息中包含指定的字符串 str
//
// 传递未初始化的 error 值(var err error = nil)，将断言失败
func (a *Assertion) ErrorString(expr error, str string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if isNil(expr) { // 空值，必定没有错误
		return a.Assert(false, NewFailure("ErrorString", msg, map[string]interface{}{"v": expr}))
	}
	return a.Assert(strings.Contains(expr.Error(), str), NewFailure("ErrorString", msg, map[string]interface{}{"v": expr}))
}

// ErrorIs 断言 expr 为 target 类型
//
// 相当于 a.True(errors.Is(expr, target))
func (a *Assertion) ErrorIs(expr, target error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(errors.Is(expr, target), NewFailure("ErrorIs", msg, map[string]interface{}{"err": expr}))
}

// NotError 断言没有错误
//
// Nil 的特化版本，限定了类型为 error。
func (a *Assertion) NotError(expr error, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isNil(expr), NewFailure("NotError", msg, map[string]interface{}{"v": expr}))
}

func (a *Assertion) FileExists(path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if _, err := os.Stat(path); err != nil && !errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileExists", msg, map[string]interface{}{"err": err}))
	}
	return a
}

func (a *Assertion) FileNotExists(path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	_, err := os.Stat(path)
	if err == nil {
		return a.Assert(false, NewFailure("FileNotExists", msg, nil))
	}
	if errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileNotExists", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) FileExistsFS(fsys fs.FS, path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if _, err := fs.Stat(fsys, path); err != nil && !errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileExistsFS", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) FileNotExistsFS(fsys fs.FS, path string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	_, err := fs.Stat(fsys, path)
	if err == nil {
		return a.Assert(false, NewFailure("FileNotExistsFS", msg, nil))
	}
	if errors.Is(err, fs.ErrExist) {
		return a.Assert(false, NewFailure("FileNotExistsFS", msg, map[string]interface{}{"err": err}))
	}

	return a
}

func (a *Assertion) Panic(fn func(), msg ...interface{}) *Assertion {
	a.TB().Helper()

	has, _ := hasPanic(fn)
	return a.Assert(has, NewFailure("Panic", msg, nil))
}

// PanicString 断言函数会发生 panic 且 panic 信息中包含指定的字符串内容
func (a *Assertion) PanicString(fn func(), str string, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if has, m := hasPanic(fn); has {
		return a.Assert(strings.Contains(fmt.Sprint(m), str), NewFailure("PanicString", msg, map[string]interface{}{"msg": m}))
	}
	return a.Assert(false, NewFailure("PanicString", msg, nil))
}

// PanicType 断言函数会发生 panic 且抛出指定的类型
func (a *Assertion) PanicType(fn func(), typ interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	if has, m := hasPanic(fn); has {
		t1, t2 := getType(true, m, typ)
		return a.Assert(t1 == t2, NewFailure("PanicType", msg, map[string]interface{}{"v1": t1, "v2": t2}))
	}
	return a.Assert(false, NewFailure("PanicType", msg, nil))
}

func (a *Assertion) NotPanic(fn func(), msg ...interface{}) *Assertion {
	a.TB().Helper()

	has, m := hasPanic(fn)
	return a.Assert(!has, NewFailure("NotPanic", msg, map[string]interface{}{"err": m}))
}

// Contains 断言 container 包含 item 或是包含 item 中的所有项
//
// 若 container 是字符串(string、[]byte 和 []rune，不包含 fmt.Stringer 接口)，
// 都将会以字符串的形式判断其是否包含 item。
// 若 container 是个列表(array、slice、map)则判断其元素中是否包含 item 中的
// 的所有项，或是 item 本身就是 container 中的一个元素。
func (a *Assertion) Contains(container, item interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isContains(container, item), NewFailure("Contains", msg, map[string]interface{}{"container": container, "item": item}))
}

// NotContains 断言 container 不包含 item 或是不包含 item 中的所有项
func (a *Assertion) NotContains(container, item interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isContains(container, item), NewFailure("NotContains", msg, map[string]interface{}{"container": container, "item": item}))
}

// Zero 断言是否为零值
//
// 最终调用的是 reflect.Value.IsZero 进行判断
func (a *Assertion) Zero(v interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	isZero := v == nil || reflect.ValueOf(v).IsZero()
	return a.Assert(isZero, NewFailure("Zero", msg, map[string]interface{}{"v": v}))
}

// NotZero 断言是否为非零值
//
// 最终调用的是 reflect.Value.IsZero 进行判断
func (a *Assertion) NotZero(v interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()

	isZero := v == nil || reflect.ValueOf(v).IsZero()
	return a.Assert(!isZero, NewFailure("NotZero", msg, map[string]interface{}{"v": v}))
}

// Length 断言长度是否为指定的值
//
// v 可以是以下类型：
//   - map
//   - string
//   - slice
//   - array
func (a *Assertion) Length(v interface{}, l int, msg ...interface{}) *Assertion {
	a.TB().Helper()

	rl, err := getLen(v)
	if err != "" {
		a.Assert(false, NewFailure("Length", msg, map[string]interface{}{"err": err}))
	}
	return a.Assert(rl == l, NewFailure("Length", msg, map[string]interface{}{"l1": rl, "l2": l}))
}

// NotLength 断言长度不是指定的值
//
// v 可以是以下类型：
//   - map
//   - string
//   - slice
//   - array
func (a *Assertion) NotLength(v interface{}, l int, msg ...interface{}) *Assertion {
	a.TB().Helper()

	rl, err := getLen(v)
	if err != "" {
		a.Assert(false, NewFailure("NotLength", msg, map[string]interface{}{"err": err}))
	}
	return a.Assert(rl != l, NewFailure("NotLength", msg, map[string]interface{}{"l": rl}))
}

// TypeEqual 断言两个值的类型是否相同
//
// ptr 如果为 true，则会在对象为指针时，查找其指向的对象。
func (a *Assertion) TypeEqual(ptr bool, v1, v2 interface{}, msg ...interface{}) *Assertion {
	if v1 == v2 {
		return a
	}

	a.TB().Helper()

	t1, t2 := getType(ptr, v1, v2)
	return a.Assert(t1 == t2, NewFailure("TypeEquaal", msg, map[string]interface{}{"v1": t1, "v2": t2}))
}

// Same 断言为同一个对象
func (a *Assertion) Same(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(isSame(v1, v2), NewFailure("Same", msg, nil))
}

// NotSame 断言为不是同一个对象
func (a *Assertion) NotSame(v1, v2 interface{}, msg ...interface{}) *Assertion {
	a.TB().Helper()
	return a.Assert(!isSame(v1, v2), NewFailure("NotSame", msg, nil))
}

func isSame(v1, v2 interface{}) bool {
	rv1 := reflect.ValueOf(v1)
	if !canPointer(rv1.Kind()) {
		return false
	}
	rv2 := reflect.ValueOf(v2)
	if !canPointer(rv2.Kind()) {
		return false
	}

	return rv1.Pointer() == rv2.Pointer()
}

func canPointer(k reflect.Kind) bool {
	switch k {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice, reflect.UnsafePointer, reflect.Func:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>// +build linux

package server

import (
	"fmt"
	"net/http"
	"os"
	"syscall"

	"github.com/docker/docker/engine"
	"github.com/docker/docker/pkg/systemd"
)

// NewServer sets up the required Server and does protocol specific checking.
func NewServer(proto, addr string, job *engine.Job) (Server, error) {
	// Basic error and sanity checking
	switch proto {
	case "fd":
		return nil, serveFd(addr, job)
	case "tcp":
		return setupTcpHttp(addr, job)
	case "unix":
		return setupUnixHttp(addr, job)
	default:
		return nil, fmt.Errorf("Invalid protocol format.")
	}
}

func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))

	if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	mask := syscall.Umask(0777)
	defer syscall.Umask(mask)

	l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
	if err != nil {
		return nil, err
	}

	if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
		return nil, err
	}

	if err := os.Chmod(addr, 0660); err != nil {
		return nil, err
	}

	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
}

// serveFd creates an http.Server and sets it up to serve given a socket activated
// argument.
func serveFd(addr string, job *engine.Job) error {
	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))

	ls, e := systemd.ListenFD(addr)
	if e != nil {
		return e
	}

	chErrors := make(chan error, len(ls))

	// We don't want to start serving on these sockets until the
	// daemon is initialized and installed. Otherwise required handlers
	// won't be ready.
	<-activationLock

	// Since ListenFD will return one or more sockets we have
	// to create a go func to spawn off multiple serves
	for i := range ls {
		listener := ls[i]
		go func() {
			httpSrv := http.Server{Handler: r}
			chErrors <- httpSrv.Serve(listener)
		}()
	}

	for i := 0; i < len(ls); i++ {
		err := <-chErrors
		if err != nil {
			return err
		}
	}

	return nil
}

// Called through eng.Job("acceptconnections")
func AcceptConnections(job *engine.Job) error {
	// Tell the init daemon we are accepting requests
	go systemd.SdNotify("READY=1")

	// close the lock so the listeners start accepting connections
	if activationLock != nil {
		close(activationLock)
	}

	return nil
}
<commit_msg>Fix panic in integration tests<commit_after>// +build linux

package server

import (
	"fmt"
	"net/http"
	"os"
	"syscall"

	"github.com/docker/docker/engine"
	"github.com/docker/docker/pkg/systemd"
)

// NewServer sets up the required Server and does protocol specific checking.
func NewServer(proto, addr string, job *engine.Job) (Server, error) {
	// Basic error and sanity checking
	switch proto {
	case "fd":
		return nil, serveFd(addr, job)
	case "tcp":
		return setupTcpHttp(addr, job)
	case "unix":
		return setupUnixHttp(addr, job)
	default:
		return nil, fmt.Errorf("Invalid protocol format.")
	}
}

func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))

	if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	mask := syscall.Umask(0777)
	defer syscall.Umask(mask)

	l, err := newListener("unix", addr, job.GetenvBool("BufferRequests"))
	if err != nil {
		return nil, err
	}

	if err := setSocketGroup(addr, job.Getenv("SocketGroup")); err != nil {
		return nil, err
	}

	if err := os.Chmod(addr, 0660); err != nil {
		return nil, err
	}

	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
}

// serveFd creates an http.Server and sets it up to serve given a socket activated
// argument.
func serveFd(addr string, job *engine.Job) error {
	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))

	ls, e := systemd.ListenFD(addr)
	if e != nil {
		return e
	}

	chErrors := make(chan error, len(ls))

	// We don't want to start serving on these sockets until the
	// daemon is initialized and installed. Otherwise required handlers
	// won't be ready.
	<-activationLock

	// Since ListenFD will return one or more sockets we have
	// to create a go func to spawn off multiple serves
	for i := range ls {
		listener := ls[i]
		go func() {
			httpSrv := http.Server{Handler: r}
			chErrors <- httpSrv.Serve(listener)
		}()
	}

	for i := 0; i < len(ls); i++ {
		err := <-chErrors
		if err != nil {
			return err
		}
	}

	return nil
}

// Called through eng.Job("acceptconnections")
func AcceptConnections(job *engine.Job) error {
	// Tell the init daemon we are accepting requests
	go systemd.SdNotify("READY=1")

	// close the lock so the listeners start accepting connections
	select {
	case <-activationLock:
	default:
		close(activationLock)
	}

	return nil
}
<|endoftext|>