<commit_before>package gpath

import (
	"errors"
	"go/ast"
	"go/constant"
	"go/parser"
	"go/token"
	"reflect"
	"strings"
)

func At(v interface{}, path string) (interface{}, error) {

	if strings.HasPrefix(path, "[") {
		path = "v" + path
	} else {
		path = "v." + path
	}

	expr, err := parser.ParseExpr(path)
	if err != nil {
		return nil, err
	}

	ev, err := at(reflect.ValueOf(v), expr)
	if err != nil {
		return nil, err
	}

	return ev.Interface(), nil
}

func at(v reflect.Value, expr ast.Expr) (reflect.Value, error) {
	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		return at(v.Elem(), expr)
	}

	switch expr := expr.(type) {
	case nil:
		return v, nil
	case *ast.Ident:
		return v, nil
	case *ast.SelectorExpr:
		return atBySelector(v, expr)
	case *ast.IndexExpr:
		return atByIndex(v, expr)
	default:
		return reflect.Value{}, errors.New("does not support expr")
	}
}

func direct(v reflect.Value) reflect.Value {
	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		return v.Elem()
	default:
		return v
	}
}

func atBySelector(v reflect.Value, expr *ast.SelectorExpr) (reflect.Value, error) {
	ev, err := at(v, expr.X)
	if err != nil {
		return reflect.Value{}, err
	}

	ev = direct(ev)
	switch ev.Kind() {
	case reflect.Struct:
		fv := ev.FieldByName(expr.Sel.Name)
		if fv == (reflect.Value{}) {
			return reflect.Value{}, errors.New("cannot find field")
		}
		return fv, nil
	default:
		return reflect.Value{}, errors.New("does not support selector type")
	}
}

func atByIndex(v reflect.Value, expr *ast.IndexExpr) (reflect.Value, error) {
	ev, err := at(v, expr.X)
	if err != nil {
		return reflect.Value{}, err
	}
	ev = direct(ev)

	bl, ok := expr.Index.(*ast.BasicLit)
	if !ok {
		return reflect.Value{}, errors.New("does not support index type")
	}

	switch ev.Kind() {
	case reflect.Slice, reflect.Array:
		i, err := intIndex(bl)
		if err != nil {
			return reflect.Value{}, err
		}
		return ev.Index(i), nil
	case reflect.Map:
		switch bl.Kind {
		case token.INT:
			k, err := intIndex(bl)
			if err != nil {
				return reflect.Value{}, err
			}
			return ev.MapIndex(reflect.ValueOf(k)), nil
		case token.STRING:
			k, err := stringIndex(bl)
			if err != nil {
				return reflect.Value{}, err
			}
			return ev.MapIndex(reflect.ValueOf(k)), nil
		default:
			return reflect.Value{}, errors.New("does not support index type")
		}
	default:
		return reflect.Value{}, errors.New("does not support expr type")
	}
}

func intIndex(bl *ast.BasicLit) (int, error) {
	if bl.Kind != token.INT {
		return 0, errors.New("does not support index type")
	}

	cv := constant.MakeFromLiteral(bl.Value, bl.Kind, 0)
	i, ok := constant.Int64Val(cv)
	if !ok {
		return 0, errors.New("does not support index type")
	}

	return int(i), nil
}

func stringIndex(bl *ast.BasicLit) (string, error) {
	if bl.Kind != token.STRING {
		return "", errors.New("does not support index type")
	}
	cv := constant.MakeFromLiteral(bl.Value, bl.Kind, 0)
	return constant.StringVal(cv), nil
}
<commit_msg>Add godoc<commit_after>package gpath

import (
	"errors"
	"go/ast"
	"go/constant"
	"go/parser"
	"go/token"
	"reflect"
	"strings"
)

// At access a field of v by a path.
// v must be struct or pointer of struct.
// A path is represented by Go's expression which can be parsed by go/parser.ParseExpr.
// You can use selectors and indexes in a path.
// Indexes allow only string and int literals for maps.
func At(v interface{}, path string) (interface{}, error) {

	if strings.HasPrefix(path, "[") {
		path = "v" + path
	} else {
		path = "v." + path
	}

	expr, err := parser.ParseExpr(path)
	if err != nil {
		return nil, err
	}

	ev, err := at(reflect.ValueOf(v), expr)
	if err != nil {
		return nil, err
	}

	return ev.Interface(), nil
}

func at(v reflect.Value, expr ast.Expr) (reflect.Value, error) {
	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		return at(v.Elem(), expr)
	}

	switch expr := expr.(type) {
	case nil:
		return v, nil
	case *ast.Ident:
		return v, nil
	case *ast.SelectorExpr:
		return atBySelector(v, expr)
	case *ast.IndexExpr:
		return atByIndex(v, expr)
	default:
		return reflect.Value{}, errors.New("does not support expr")
	}
}

func direct(v reflect.Value) reflect.Value {
	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		return v.Elem()
	default:
		return v
	}
}

func atBySelector(v reflect.Value, expr *ast.SelectorExpr) (reflect.Value, error) {
	ev, err := at(v, expr.X)
	if err != nil {
		return reflect.Value{}, err
	}

	ev = direct(ev)
	switch ev.Kind() {
	case reflect.Struct:
		fv := ev.FieldByName(expr.Sel.Name)
		if fv == (reflect.Value{}) {
			return reflect.Value{}, errors.New("cannot find field")
		}
		return fv, nil
	default:
		return reflect.Value{}, errors.New("does not support selector type")
	}
}

func atByIndex(v reflect.Value, expr *ast.IndexExpr) (reflect.Value, error) {
	ev, err := at(v, expr.X)
	if err != nil {
		return reflect.Value{}, err
	}
	ev = direct(ev)

	bl, ok := expr.Index.(*ast.BasicLit)
	if !ok {
		return reflect.Value{}, errors.New("does not support index type")
	}

	switch ev.Kind() {
	case reflect.Slice, reflect.Array:
		i, err := intIndex(bl)
		if err != nil {
			return reflect.Value{}, err
		}
		return ev.Index(i), nil
	case reflect.Map:
		switch bl.Kind {
		case token.INT:
			k, err := intIndex(bl)
			if err != nil {
				return reflect.Value{}, err
			}
			return ev.MapIndex(reflect.ValueOf(k)), nil
		case token.STRING:
			k, err := stringIndex(bl)
			if err != nil {
				return reflect.Value{}, err
			}
			return ev.MapIndex(reflect.ValueOf(k)), nil
		default:
			return reflect.Value{}, errors.New("does not support index type")
		}
	default:
		return reflect.Value{}, errors.New("does not support expr type")
	}
}

func intIndex(bl *ast.BasicLit) (int, error) {
	if bl.Kind != token.INT {
		return 0, errors.New("does not support index type")
	}

	cv := constant.MakeFromLiteral(bl.Value, bl.Kind, 0)
	i, ok := constant.Int64Val(cv)
	if !ok {
		return 0, errors.New("does not support index type")
	}

	return int(i), nil
}

func stringIndex(bl *ast.BasicLit) (string, error) {
	if bl.Kind != token.STRING {
		return "", errors.New("does not support index type")
	}
	cv := constant.MakeFromLiteral(bl.Value, bl.Kind, 0)
	return constant.StringVal(cv), nil
}
<|endoftext|>


<commit_before>package gogl

import "fmt"

// for great justice
var fml = fmt.Println

/* Composite graph interfaces */

// Graph is gogl's most basic interface: it contains only the methods that
// *every* type of graph implements.
//
// Graph is intentionally underspecified: both directed and undirected graphs
// implement it; simple graphs, multigraphs, weighted, labeled, or any
// combination thereof.
//
// The semantics of some of these methods vary slightly from one graph type
// to another, but in general, the basic Graph methods are supplemented, not
// superceded, by the methods in more specific interfaces.
//
// Graph is a purely read oriented interface; the various Mutable*Graph
// interfaces contain the methods for writing.
type Graph interface {
	VertexEnumerator        // Enumerates vertices to an injected lambda
	EdgeEnumerator          // Enumerates edges to an injected lambda
	AdjacencyEnumerator     // Enumerates a vertex's adjacent vertices to an injected lambda
	IncidentEdgeEnumerator  // Enumerates a vertex's incident edges to an injected lambda
	VertexMembershipChecker // Allows inspection of contained vertices
	EdgeMembershipChecker   // Allows inspection of contained edges
	DegreeChecker           // Reports degree of vertices
	Order() int             // Reports total number of vertices in the graph
	Size() int              // Reports total number of edges in the graph
}

// GraphSource is a subinterface of Graph, describing the minimal set of methods
// necessary to accomplish a naive full graph traversal and copy.
type GraphSource interface {
	VertexEnumerator
	EdgeEnumerator
	Order() int
}

// DirectedGraph describes a Graph all of whose edges are directed.
//
// gogl treats edge directionality as a property of the graph, not the edge itself.
// Thus, implementing this interface is gogl's only signal that a graph's edges are directed.
type DirectedGraph interface {
	Graph
	IncidentArcEnumerator // Enumerates a vertex's incident in- and out-arcs to an injected lambda
	DirectedDegreeChecker // Reports in- and out-degree of vertices
	Transposer            // DirectedGraphs can produce a transpose of themselves
}

// MutableGraph describes a graph with basic edges (no weighting, labeling, etc.)
// that can be modified freely by adding or removing vertices or edges.
type MutableGraph interface {
	Graph
	VertexSetMutator
	EdgeSetMutator
}

// A simple graph is in opposition to a multigraph: it disallows loops and
// parallel edges.
type SimpleGraph interface {
	Graph
	Density() float64
}

// A weighted graph is a graph subtype where the edges have a numeric weight;
// as described by the WeightedEdge interface, this weight is a signed int.
//
// WeightedGraphs have both the HasEdge() and HasWeightedEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its weight.
//
// HasWeightedEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge weights are the same.
type WeightedGraph interface {
	Graph
	HasWeightedEdge(e WeightedEdge) bool
}

// MutableWeightedGraph is the mutable version of a weighted graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only weighted edges can be present in the graph.
type MutableWeightedGraph interface {
	WeightedGraph
	VertexSetMutator
	WeightedEdgeSetMutator
}

// A labeled graph is a graph subtype where the edges have an identifier;
// as described by the LabeledEdge interface, this identifier is a string.
//
// LabeledGraphs have both the HasEdge() and HasLabeledEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its label.
//
// HasLabeledEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge labels are the same.
type LabeledGraph interface {
	Graph
	HasLabeledEdge(e LabeledEdge) bool
}

// LabeledWeightedGraph is the mutable version of a labeled graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only labeled edges can be present in the graph.
type MutableLabeledGraph interface {
	LabeledGraph
	VertexSetMutator
	LabeledEdgeSetMutator
}

// A data graph is a graph subtype where the edges carry arbitrary Go data;
// as described by the DataEdge interface, this identifier is an interface{}.
//
// DataGraphs have both the HasEdge() and HasDataEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its label.
//
// HasDataEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge data is the same. Simple comparison will typically be used
// to establish data equality, which means that using noncomparables (a slice,
// map, or non-pointer struct containing a slice or a map) for the data will
// cause a panic.
type DataGraph interface {
	Graph
	HasDataEdge(e DataEdge) bool
}

// MutableDataGraph is the mutable version of a propety graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only property edges can be present in the graph.
type MutableDataGraph interface {
	DataGraph
	VertexSetMutator
	DataEdgeSetMutator
}
/* Atomic graph interfaces */

// EdgeLambdas are used as arguments to various enumerators. They are called once for each edge produced by the enumerator.
//
// If the lambda returns true, the calling enumerator is expected to end enumeration and return control to its caller.
type EdgeLambda func(Edge) (terminate bool)

// VertexLambdas are used as arguments to various enumerators. They are called once for each vertex produced by the enumerator.
//
// If the lambda returns true, the calling enumerator is expected to end enumeration and return control to its caller.
type VertexLambda func(Vertex) (terminate bool)

// A VertexEnumerator iteratively enumerates vertices, and can indicate the number of vertices present.
type VertexEnumerator interface {
	EachVertex(VertexLambda)
}

// An EdgeEnumerator iteratively enumerates edges, and can indicate the number of edges present.
type EdgeEnumerator interface {
	EachEdge(EdgeLambda)
}

// An IncidentEdgeEnumerator iteratively enumerates a given vertex's incident edges.
type IncidentEdgeEnumerator interface {
	EachEdgeIncidentTo(v Vertex, incidentEdgeLambda EdgeLambda)
}

// An IncidentArcEnumerator iteratively enumerates a given vertex's incident arcs (directed edges).
// One enumerator provides inbound edges, the other outbound edges.
type IncidentArcEnumerator interface {
	EachArcFrom(v Vertex, outEdgeLambda EdgeLambda)
	EachArcTo(v Vertex, inEdgeLambda EdgeLambda)
}

// An AdjacencyEnumerator iteratively enumerates a given vertex's adjacent vertices.
type AdjacencyEnumerator interface {
	EachAdjacentTo(start Vertex, adjacentVertexLambda VertexLambda)
}

// A VertexMembershipChecker can indicate the presence of a vertex.
type VertexMembershipChecker interface {
	HasVertex(Vertex) bool // Whether or not the vertex is present in the set
}

// A DegreeChecker reports the number of edges incident to a given vertex.
type DegreeChecker interface {
	DegreeOf(Vertex) (degree int, exists bool) // Number of incident edges; if vertex is present
}

// A DirectedDegreeChecker reports the number of in or out-edges incident to given vertex.
type DirectedDegreeChecker interface {
	InDegreeOf(Vertex) (degree int, exists bool)  // Number of in-edges; if vertex is present
	OutDegreeOf(Vertex) (degree int, exists bool) // Number of out-edges; if vertex is present
}

// An EdgeMembershipChecker can indicate the presence of an edge.
type EdgeMembershipChecker interface {
	HasEdge(Edge) bool
}

// A VertexSetMutator allows the addition and removal of vertices from a set.
type VertexSetMutator interface {
	EnsureVertex(...Vertex)
	RemoveVertex(...Vertex)
}

// An EdgeSetMutator allows the addition and removal of edges from a set.
type EdgeSetMutator interface {
	AddEdges(edges ...Edge)
	RemoveEdges(edges ...Edge)
}

// A WeightedEdgeSetMutator allows the addition and removal of weighted edges from a set.
type WeightedEdgeSetMutator interface {
	AddEdges(edges ...WeightedEdge)
	RemoveEdges(edges ...WeightedEdge)
}

// A LabeledEdgeSetMutator allows the addition and removal of labeled edges from a set.
type LabeledEdgeSetMutator interface {
	AddEdges(edges ...LabeledEdge)
	RemoveEdges(edges ...LabeledEdge)
}

// A DataEdgeSetMutator allows the addition and removal of data edges from a set.
type DataEdgeSetMutator interface {
	AddEdges(edges ...DataEdge)
	RemoveEdges(edges ...DataEdge)
}

// A Transposer produces a transposed version of a DirectedGraph.
type Transposer interface {
	Transpose() DirectedGraph
}
<commit_msg>doc touchup<commit_after>package gogl

import "fmt"

// for great justice
var fml = fmt.Println

/* Composite graph interfaces */

// Graph is gogl's most basic interface: it contains only the methods that
// *every* type of graph implements.
//
// Graph is intentionally underspecified: both directed and undirected graphs
// implement it; simple graphs, multigraphs, weighted, labeled, or any
// combination thereof.
//
// The semantics of some of these methods vary slightly from one graph type
// to another, but in general, the basic Graph methods are supplemented, not
// superceded, by the methods in more specific interfaces.
//
// Graph is a purely read oriented interface; the various Mutable*Graph
// interfaces contain the methods for writing.
type Graph interface {
	VertexEnumerator        // Enumerates vertices to an injected lambda
	EdgeEnumerator          // Enumerates edges to an injected lambda
	AdjacencyEnumerator     // Enumerates a vertex's adjacent vertices to an injected lambda
	IncidentEdgeEnumerator  // Enumerates a vertex's incident edges to an injected lambda
	VertexMembershipChecker // Allows inspection of contained vertices
	EdgeMembershipChecker   // Allows inspection of contained edges
	DegreeChecker           // Reports degree of vertices
	Order() int             // Reports total number of vertices in the graph
	Size() int              // Reports total number of edges in the graph
}

// GraphSource is a subinterface of Graph, describing the minimal set of methods
// necessary to accomplish a naive full graph traversal and copy.
type GraphSource interface {
	VertexEnumerator
	EdgeEnumerator
	Order() int
}

// DirectedGraph describes a Graph all of whose edges are directed.
//
// gogl treats edge directionality as a property of the graph, not the edge itself.
// Thus, implementing this interface is gogl's only signal that a graph's edges are directed.
type DirectedGraph interface {
	Graph
	IncidentArcEnumerator // Enumerates a vertex's incident in- and out-arcs to an injected lambda
	DirectedDegreeChecker // Reports in- and out-degree of vertices
	Transposer            // DirectedGraphs can produce a transpose of themselves
}

// MutableGraph describes a graph with basic edges (no weighting, labeling, etc.)
// that can be modified freely by adding or removing vertices or edges.
type MutableGraph interface {
	Graph
	VertexSetMutator
	EdgeSetMutator
}

// A simple graph is in opposition to a multigraph or pseudograph: it disallows loops and
// parallel edges.
type SimpleGraph interface {
	Graph
	Density() float64
}

// A weighted graph is a graph subtype where the edges have a numeric weight;
// as described by the WeightedEdge interface, this weight is a signed int.
//
// WeightedGraphs have both the HasEdge() and HasWeightedEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its weight.
//
// HasWeightedEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge weights are the same.
type WeightedGraph interface {
	Graph
	HasWeightedEdge(e WeightedEdge) bool
}

// MutableWeightedGraph is the mutable version of a weighted graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only weighted edges can be present in the graph.
type MutableWeightedGraph interface {
	WeightedGraph
	VertexSetMutator
	WeightedEdgeSetMutator
}

// A labeled graph is a graph subtype where the edges have an identifier;
// as described by the LabeledEdge interface, this identifier is a string.
//
// LabeledGraphs have both the HasEdge() and HasLabeledEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its label.
//
// HasLabeledEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge labels are the same.
type LabeledGraph interface {
	Graph
	HasLabeledEdge(e LabeledEdge) bool
}

// LabeledWeightedGraph is the mutable version of a labeled graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only labeled edges can be present in the graph.
type MutableLabeledGraph interface {
	LabeledGraph
	VertexSetMutator
	LabeledEdgeSetMutator
}

// A data graph is a graph subtype where the edges carry arbitrary Go data;
// as described by the DataEdge interface, this identifier is an interface{}.
//
// DataGraphs have both the HasEdge() and HasDataEdge() methods.
// Correct implementations should treat the difference as a matter of strictness:
//
// HasEdge() should return true as long as an edge exists
// connecting the two given vertices (respecting directed or undirected as
// appropriate), regardless of its label.
//
// HasDataEdge() should return true iff an edge exists connecting the
// two given vertices (respecting directed or undirected as appropriate),
// AND if the edge data is the same. Simple comparison will typically be used
// to establish data equality, which means that using noncomparables (a slice,
// map, or non-pointer struct containing a slice or a map) for the data will
// cause a panic.
type DataGraph interface {
	Graph
	HasDataEdge(e DataEdge) bool
}

// MutableDataGraph is the mutable version of a propety graph. Its
// AddEdges() method is incompatible with MutableGraph, guaranteeing
// only property edges can be present in the graph.
type MutableDataGraph interface {
	DataGraph
	VertexSetMutator
	DataEdgeSetMutator
}
/* Atomic graph interfaces */

// EdgeLambdas are used as arguments to various enumerators. They are called once for each edge produced by the enumerator.
//
// If the lambda returns true, the calling enumerator is expected to end enumeration and return control to its caller.
type EdgeLambda func(Edge) (terminate bool)

// VertexLambdas are used as arguments to various enumerators. They are called once for each vertex produced by the enumerator.
//
// If the lambda returns true, the calling enumerator is expected to end enumeration and return control to its caller.
type VertexLambda func(Vertex) (terminate bool)

// A VertexEnumerator iteratively enumerates vertices, and can indicate the number of vertices present.
type VertexEnumerator interface {
	EachVertex(VertexLambda)
}

// An EdgeEnumerator iteratively enumerates edges, and can indicate the number of edges present.
type EdgeEnumerator interface {
	EachEdge(EdgeLambda)
}

// An IncidentEdgeEnumerator iteratively enumerates a given vertex's incident edges.
type IncidentEdgeEnumerator interface {
	EachEdgeIncidentTo(v Vertex, incidentEdgeLambda EdgeLambda)
}

// An IncidentArcEnumerator iteratively enumerates a given vertex's incident arcs (directed edges).
// One enumerator provides inbound edges, the other outbound edges.
type IncidentArcEnumerator interface {
	EachArcFrom(v Vertex, outEdgeLambda EdgeLambda)
	EachArcTo(v Vertex, inEdgeLambda EdgeLambda)
}

// An AdjacencyEnumerator iteratively enumerates a given vertex's adjacent vertices.
type AdjacencyEnumerator interface {
	EachAdjacentTo(start Vertex, adjacentVertexLambda VertexLambda)
}

// A VertexMembershipChecker can indicate the presence of a vertex.
type VertexMembershipChecker interface {
	HasVertex(Vertex) bool // Whether or not the vertex is present in the set
}

// A DegreeChecker reports the number of edges incident to a given vertex.
type DegreeChecker interface {
	DegreeOf(Vertex) (degree int, exists bool) // Number of incident edges; if vertex is present
}

// A DirectedDegreeChecker reports the number of in or out-edges incident to given vertex.
type DirectedDegreeChecker interface {
	InDegreeOf(Vertex) (degree int, exists bool)  // Number of in-edges; if vertex is present
	OutDegreeOf(Vertex) (degree int, exists bool) // Number of out-edges; if vertex is present
}

// An EdgeMembershipChecker can indicate the presence of an edge.
type EdgeMembershipChecker interface {
	HasEdge(Edge) bool
}

// A VertexSetMutator allows the addition and removal of vertices from a set.
type VertexSetMutator interface {
	EnsureVertex(...Vertex)
	RemoveVertex(...Vertex)
}

// An EdgeSetMutator allows the addition and removal of edges from a set.
type EdgeSetMutator interface {
	AddEdges(edges ...Edge)
	RemoveEdges(edges ...Edge)
}

// A WeightedEdgeSetMutator allows the addition and removal of weighted edges from a set.
type WeightedEdgeSetMutator interface {
	AddEdges(edges ...WeightedEdge)
	RemoveEdges(edges ...WeightedEdge)
}

// A LabeledEdgeSetMutator allows the addition and removal of labeled edges from a set.
type LabeledEdgeSetMutator interface {
	AddEdges(edges ...LabeledEdge)
	RemoveEdges(edges ...LabeledEdge)
}

// A DataEdgeSetMutator allows the addition and removal of data edges from a set.
type DataEdgeSetMutator interface {
	AddEdges(edges ...DataEdge)
	RemoveEdges(edges ...DataEdge)
}

// A Transposer produces a transposed version of a DirectedGraph.
type Transposer interface {
	Transpose() DirectedGraph
}
<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 ns

import (
	"crypto/rand"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"

	"golang.org/x/sys/unix"
)

type NetNS interface {
	// Executes the passed closure in this object's network namespace,
	// attemtping to restore the original namespace before returning.
	// However, since each OS thread can have a different network namespace,
	// and Go's thread scheduling is highly variable, callers cannot
	// guarantee any specific namespace is set unless operations that
	// require that namespace are wrapped with Do().  Also, no code called
	// from Do() should call runtime.UnlockOSThread(), or the risk
	// of executing code in an incorrect namespace will be greater.  See
	// https://github.com/golang/go/wiki/LockOSThread for further details.
	Do(toRun func(NetNS) error) error

	// Sets the current network namespace to this object's network namespace.
	// Note that since Go's thread scheduling is highly variable, callers
	// cannot guarantee the requested namespace will be the current namespace
	// after this function is called; to ensure this wrap operations that
	// require the namespace with Do() instead.
	Set() error

	// Returns the filesystem path representing this object's network namespace
	Path() string

	// Returns a file descriptor representing this object's network namespace
	Fd() uintptr

	// Cleans up this instance of the network namespace; if this instance
	// is the last user the namespace will be destroyed
	Close() error
}

type netNS struct {
	file    *os.File
	mounted bool
	closed  bool
}

func getCurrentThreadNetNSPath() string {
	// /proc/self/ns/net returns the namespace of the main thread, not
	// of whatever thread this goroutine is running on.  Make sure we
	// use the thread's net namespace since the thread is switching around
	return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}

// Returns an object representing the current OS thread's network namespace
func GetCurrentNS() (NetNS, error) {
	return GetNS(getCurrentThreadNetNSPath())
}

const (
	// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
	NSFS_MAGIC   = 0x6e736673
	PROCFS_MAGIC = 0x9fa0
)

type NSPathNotExistErr struct{ msg string }

func (e NSPathNotExistErr) Error() string { return e.msg }

type NSPathNotNSErr struct{ msg string }

func (e NSPathNotNSErr) Error() string { return e.msg }

func IsNSorErr(nspath string) error {
	stat := syscall.Statfs_t{}
	if err := syscall.Statfs(nspath, &stat); err != nil {
		if os.IsNotExist(err) {
			err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)}
		} else {
			err = fmt.Errorf("failed to Statfs %q: %v", nspath, err)
		}
		return err
	}

	switch stat.Type {
	case PROCFS_MAGIC:
		// Kernel < 3.19

		validPathContent := "ns/"
		validName := strings.Contains(nspath, validPathContent)
		if !validName {
			return NSPathNotNSErr{msg: fmt.Sprintf("path %q doesn't contain %q", nspath, validPathContent)}
		}

		return nil
	case NSFS_MAGIC:
		// Kernel >= 3.19

		return nil
	default:
		return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
	}
}

// Returns an object representing the namespace referred to by @path
func GetNS(nspath string) (NetNS, error) {
	err := IsNSorErr(nspath)
	if err != nil {
		return nil, err
	}

	fd, err := os.Open(nspath)
	if err != nil {
		return nil, err
	}

	return &netNS{file: fd}, nil
}

// Creates a new persistent network namespace and returns an object
// representing that namespace, without switching to it
func NewNS() (NetNS, error) {
	const nsRunDir = "/var/run/netns"

	b := make([]byte, 16)
	_, err := rand.Reader.Read(b)
	if err != nil {
		return nil, fmt.Errorf("failed to generate random netns name: %v", err)
	}

	err = os.MkdirAll(nsRunDir, 0755)
	if err != nil {
		return nil, err
	}

	// create an empty file at the mount point
	nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
	nsPath := path.Join(nsRunDir, nsName)
	mountPointFd, err := os.Create(nsPath)
	if err != nil {
		return nil, err
	}
	mountPointFd.Close()

	// Ensure the mount point is cleaned up on errors; if the namespace
	// was successfully mounted this will have no effect because the file
	// is in-use
	defer os.RemoveAll(nsPath)

	var wg sync.WaitGroup
	wg.Add(1)

	// do namespace work in a dedicated goroutine, so that we can safely
	// Lock/Unlock OSThread without upsetting the lock/unlock state of
	// the caller of this function
	var fd *os.File
	go (func() {
		defer wg.Done()
		runtime.LockOSThread()

		var origNS NetNS
		origNS, err = GetNS(getCurrentThreadNetNSPath())
		if err != nil {
			return
		}
		defer origNS.Close()

		// create a new netns on the current thread
		err = unix.Unshare(unix.CLONE_NEWNET)
		if err != nil {
			return
		}
		defer origNS.Set()

		// bind mount the new netns from the current thread onto the mount point
		err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
		if err != nil {
			return
		}

		fd, err = os.Open(nsPath)
		if err != nil {
			return
		}
	})()
	wg.Wait()

	if err != nil {
		unix.Unmount(nsPath, unix.MNT_DETACH)
		return nil, fmt.Errorf("failed to create namespace: %v", err)
	}

	return &netNS{file: fd, mounted: true}, nil
}

func (ns *netNS) Path() string {
	return ns.file.Name()
}

func (ns *netNS) Fd() uintptr {
	return ns.file.Fd()
}

func (ns *netNS) errorIfClosed() error {
	if ns.closed {
		return fmt.Errorf("%q has already been closed", ns.file.Name())
	}
	return nil
}

func (ns *netNS) Close() error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	if err := ns.file.Close(); err != nil {
		return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
	}
	ns.closed = true

	if ns.mounted {
		if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
			return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
		}
		if err := os.RemoveAll(ns.file.Name()); err != nil {
			return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
		}
		ns.mounted = false
	}

	return nil
}

func (ns *netNS) Do(toRun func(NetNS) error) error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	containedCall := func(hostNS NetNS) error {
		threadNS, err := GetNS(getCurrentThreadNetNSPath())
		if err != nil {
			return fmt.Errorf("failed to open current netns: %v", err)
		}
		defer threadNS.Close()

		// switch to target namespace
		if err = ns.Set(); err != nil {
			return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
		}
		defer threadNS.Set() // switch back

		return toRun(hostNS)
	}

	// save a handle to current network namespace
	hostNS, err := GetNS(getCurrentThreadNetNSPath())
	if err != nil {
		return fmt.Errorf("Failed to open current namespace: %v", err)
	}
	defer hostNS.Close()

	var wg sync.WaitGroup
	wg.Add(1)

	var innerError error
	go func() {
		defer wg.Done()
		runtime.LockOSThread()
		innerError = containedCall(hostNS)
	}()
	wg.Wait()

	return innerError
}

func (ns *netNS) Set() error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	if _, _, err := unix.Syscall(unix.SYS_SETNS, ns.Fd(), uintptr(unix.CLONE_NEWNET), 0); err != 0 {
		return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
	}

	return nil
}

// WithNetNSPath executes the passed closure under the given network
// namespace, restoring the original namespace afterwards.
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
	ns, err := GetNS(nspath)
	if err != nil {
		return err
	}
	defer ns.Close()
	return ns.Do(toRun)
}
<commit_msg>pkg/ns: fix misspelling in comment<commit_after>// Copyright 2015 CNI 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 ns

import (
	"crypto/rand"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"

	"golang.org/x/sys/unix"
)

type NetNS interface {
	// Executes the passed closure in this object's network namespace,
	// attempting to restore the original namespace before returning.
	// However, since each OS thread can have a different network namespace,
	// and Go's thread scheduling is highly variable, callers cannot
	// guarantee any specific namespace is set unless operations that
	// require that namespace are wrapped with Do().  Also, no code called
	// from Do() should call runtime.UnlockOSThread(), or the risk
	// of executing code in an incorrect namespace will be greater.  See
	// https://github.com/golang/go/wiki/LockOSThread for further details.
	Do(toRun func(NetNS) error) error

	// Sets the current network namespace to this object's network namespace.
	// Note that since Go's thread scheduling is highly variable, callers
	// cannot guarantee the requested namespace will be the current namespace
	// after this function is called; to ensure this wrap operations that
	// require the namespace with Do() instead.
	Set() error

	// Returns the filesystem path representing this object's network namespace
	Path() string

	// Returns a file descriptor representing this object's network namespace
	Fd() uintptr

	// Cleans up this instance of the network namespace; if this instance
	// is the last user the namespace will be destroyed
	Close() error
}

type netNS struct {
	file    *os.File
	mounted bool
	closed  bool
}

func getCurrentThreadNetNSPath() string {
	// /proc/self/ns/net returns the namespace of the main thread, not
	// of whatever thread this goroutine is running on.  Make sure we
	// use the thread's net namespace since the thread is switching around
	return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}

// Returns an object representing the current OS thread's network namespace
func GetCurrentNS() (NetNS, error) {
	return GetNS(getCurrentThreadNetNSPath())
}

const (
	// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
	NSFS_MAGIC   = 0x6e736673
	PROCFS_MAGIC = 0x9fa0
)

type NSPathNotExistErr struct{ msg string }

func (e NSPathNotExistErr) Error() string { return e.msg }

type NSPathNotNSErr struct{ msg string }

func (e NSPathNotNSErr) Error() string { return e.msg }

func IsNSorErr(nspath string) error {
	stat := syscall.Statfs_t{}
	if err := syscall.Statfs(nspath, &stat); err != nil {
		if os.IsNotExist(err) {
			err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)}
		} else {
			err = fmt.Errorf("failed to Statfs %q: %v", nspath, err)
		}
		return err
	}

	switch stat.Type {
	case PROCFS_MAGIC:
		// Kernel < 3.19

		validPathContent := "ns/"
		validName := strings.Contains(nspath, validPathContent)
		if !validName {
			return NSPathNotNSErr{msg: fmt.Sprintf("path %q doesn't contain %q", nspath, validPathContent)}
		}

		return nil
	case NSFS_MAGIC:
		// Kernel >= 3.19

		return nil
	default:
		return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
	}
}

// Returns an object representing the namespace referred to by @path
func GetNS(nspath string) (NetNS, error) {
	err := IsNSorErr(nspath)
	if err != nil {
		return nil, err
	}

	fd, err := os.Open(nspath)
	if err != nil {
		return nil, err
	}

	return &netNS{file: fd}, nil
}

// Creates a new persistent network namespace and returns an object
// representing that namespace, without switching to it
func NewNS() (NetNS, error) {
	const nsRunDir = "/var/run/netns"

	b := make([]byte, 16)
	_, err := rand.Reader.Read(b)
	if err != nil {
		return nil, fmt.Errorf("failed to generate random netns name: %v", err)
	}

	err = os.MkdirAll(nsRunDir, 0755)
	if err != nil {
		return nil, err
	}

	// create an empty file at the mount point
	nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
	nsPath := path.Join(nsRunDir, nsName)
	mountPointFd, err := os.Create(nsPath)
	if err != nil {
		return nil, err
	}
	mountPointFd.Close()

	// Ensure the mount point is cleaned up on errors; if the namespace
	// was successfully mounted this will have no effect because the file
	// is in-use
	defer os.RemoveAll(nsPath)

	var wg sync.WaitGroup
	wg.Add(1)

	// do namespace work in a dedicated goroutine, so that we can safely
	// Lock/Unlock OSThread without upsetting the lock/unlock state of
	// the caller of this function
	var fd *os.File
	go (func() {
		defer wg.Done()
		runtime.LockOSThread()

		var origNS NetNS
		origNS, err = GetNS(getCurrentThreadNetNSPath())
		if err != nil {
			return
		}
		defer origNS.Close()

		// create a new netns on the current thread
		err = unix.Unshare(unix.CLONE_NEWNET)
		if err != nil {
			return
		}
		defer origNS.Set()

		// bind mount the new netns from the current thread onto the mount point
		err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
		if err != nil {
			return
		}

		fd, err = os.Open(nsPath)
		if err != nil {
			return
		}
	})()
	wg.Wait()

	if err != nil {
		unix.Unmount(nsPath, unix.MNT_DETACH)
		return nil, fmt.Errorf("failed to create namespace: %v", err)
	}

	return &netNS{file: fd, mounted: true}, nil
}

func (ns *netNS) Path() string {
	return ns.file.Name()
}

func (ns *netNS) Fd() uintptr {
	return ns.file.Fd()
}

func (ns *netNS) errorIfClosed() error {
	if ns.closed {
		return fmt.Errorf("%q has already been closed", ns.file.Name())
	}
	return nil
}

func (ns *netNS) Close() error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	if err := ns.file.Close(); err != nil {
		return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
	}
	ns.closed = true

	if ns.mounted {
		if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
			return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
		}
		if err := os.RemoveAll(ns.file.Name()); err != nil {
			return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
		}
		ns.mounted = false
	}

	return nil
}

func (ns *netNS) Do(toRun func(NetNS) error) error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	containedCall := func(hostNS NetNS) error {
		threadNS, err := GetNS(getCurrentThreadNetNSPath())
		if err != nil {
			return fmt.Errorf("failed to open current netns: %v", err)
		}
		defer threadNS.Close()

		// switch to target namespace
		if err = ns.Set(); err != nil {
			return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
		}
		defer threadNS.Set() // switch back

		return toRun(hostNS)
	}

	// save a handle to current network namespace
	hostNS, err := GetNS(getCurrentThreadNetNSPath())
	if err != nil {
		return fmt.Errorf("Failed to open current namespace: %v", err)
	}
	defer hostNS.Close()

	var wg sync.WaitGroup
	wg.Add(1)

	var innerError error
	go func() {
		defer wg.Done()
		runtime.LockOSThread()
		innerError = containedCall(hostNS)
	}()
	wg.Wait()

	return innerError
}

func (ns *netNS) Set() error {
	if err := ns.errorIfClosed(); err != nil {
		return err
	}

	if _, _, err := unix.Syscall(unix.SYS_SETNS, ns.Fd(), uintptr(unix.CLONE_NEWNET), 0); err != 0 {
		return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
	}

	return nil
}

// WithNetNSPath executes the passed closure under the given network
// namespace, restoring the original namespace afterwards.
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
	ns, err := GetNS(nspath)
	if err != nil {
		return err
	}
	defer ns.Close()
	return ns.Do(toRun)
}
<|endoftext|>


<commit_before>package stripe

import (
	"encoding/json"
)

// OrderStatus represents the statuses of an order object.
type OrderStatus string

// List of values that OrderStatus can take.
const (
	OrderStatusCanceled  OrderStatus = "canceled"
	OrderStatusCreated   OrderStatus = "created"
	OrderStatusFulfilled OrderStatus = "fulfilled"
	OrderStatusPaid      OrderStatus = "paid"
	OrderStatusReturned  OrderStatus = "returned"
)

// OrderDeliveryEstimateType represents the type of delivery estimate for shipping methods
type OrderDeliveryEstimateType string

// List of values that OrderDeliveryEstimateType can take.
const (
	OrderDeliveryEstimateTypeExact OrderDeliveryEstimateType = "exact"
	OrderDeliveryEstimateTypeRange OrderDeliveryEstimateType = "range"
)

// OrderItemType represents the type of order item
type OrderItemType string

// List of values that OrderItemType can take.
const (
	OrderItemTypeCoupon   OrderItemType = "coupon"
	OrderItemTypeDiscount OrderItemType = "discount"
	OrderItemTypeSKU      OrderItemType = "sku"
	OrderItemTypeShipping OrderItemType = "shipping"
	OrderItemTypeTax      OrderItemType = "tax"
)

// OrderItemParentType represents the type of order item parent
type OrderItemParentType string

// List of values that OrderItemParentType can take.
const (
	OrderItemParentTypeCoupon   OrderItemParentType = "coupon"
	OrderItemParentTypeDiscount OrderItemParentType = "discount"
	OrderItemParentTypeSKU      OrderItemParentType = "sku"
	OrderItemParentTypeShipping OrderItemParentType = "shipping"
	OrderItemParentTypeTax      OrderItemParentType = "tax"
)

// OrderItemParent describes the parent of an order item.
type OrderItemParent struct {
	ID   string              `json:"id"`
	SKU  *SKU                `json:"-"`
	Type OrderItemParentType `json:"object"`
}

// OrderParams is the set of parameters that can be used when creating an order.
type OrderParams struct {
	Params   `form:"*"`
	Coupon   *string            `form:"coupon"`
	Currency *string            `form:"currency"`
	Customer *string            `form:"customer"`
	Email    *string            `form:"email"`
	Items    []*OrderItemParams `form:"items"`
	Shipping *ShippingParams    `form:"shipping"`
}

// ShippingParams is the set of parameters that can be used for the shipping hash
// on order creation.
type ShippingParams struct {
	Address *AddressParams `form:"address"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// OrderUpdateParams is the set of parameters that can be used when updating an order.
type OrderUpdateParams struct {
	Params                 `form:"*"`
	Coupon                 *string                    `form:"coupon"`
	SelectedShippingMethod *string                    `form:"selected_shipping_method"`
	Shipping               *OrderUpdateShippingParams `form:"shipping"`
	Status                 *string                    `form:"status"`
}

// OrderUpdateShippingParams is the set of parameters that can be used for the shipping
// hash on order update.
type OrderUpdateShippingParams struct {
	Carrier        *string `form:"carrier"`
	TrackingNumber *string `form:"tracking_number"`
}

// Shipping describes the shipping hash on an order.
type Shipping struct {
	Address        *Address `json:"address"`
	Carrier        string   `json:"carrier"`
	Name           string   `json:"name"`
	Phone          string   `json:"phone"`
	TrackingNumber string   `json:"tracking_number"`
}

// ShippingMethod describes a shipping method as available on an order.
type ShippingMethod struct {
	Amount           int64             `json:"amount"`
	ID               string            `json:"id"`
	Currency         Currency          `json:"currency"`
	DeliveryEstimate *DeliveryEstimate `json:"delivery_estimate"`
	Description      string            `json:"description"`
}

// DeliveryEstimate represent the properties available for a shipping method's
// estimated delivery.
type DeliveryEstimate struct {
	// If Type == Exact
	Date string `json:"date"`
	// If Type == Range
	Earliest string                    `json:"earliest"`
	Latest   string                    `json:"latest"`
	Type     OrderDeliveryEstimateType `json:"type"`
}

// Order is the resource representing a Stripe charge.
// For more details see https://stripe.com/docs/api#orders.
type Order struct {
	Amount                 int64             `json:"amount"`
	AmountReturned         int64             `json:"amount_returned"`
	Application            string            `json:"application"`
	ApplicationFee         int64             `json:"application_fee"`
	Charge                 *Charge           `json:"charge"`
	Created                int64             `json:"created"`
	Currency               Currency          `json:"currency"`
	Customer               Customer          `json:"customer"`
	Email                  string            `json:"email"`
	ID                     string            `json:"id"`
	Items                  []*OrderItem      `json:"items"`
	Livemode               bool              `json:"livemode"`
	Metadata               map[string]string `json:"metadata"`
	Returns                *OrderReturnList  `json:"returns"`
	SelectedShippingMethod *string           `json:"selected_shipping_method"`
	Shipping               *Shipping         `json:"shipping"`
	ShippingMethods        []*ShippingMethod `json:"shipping_methods"`
	Status                 string            `json:"status"`
	StatusTransitions      StatusTransitions `json:"status_transitions"`
	Updated                int64             `json:"updated"`
	UpstreamID             string            `json:"upstream_id"`
}

// OrderList is a list of orders as retrieved from a list endpoint.
type OrderList struct {
	ListMeta
	Data []*Order `json:"data"`
}

// OrderListParams is the set of parameters that can be used when listing orders.
type OrderListParams struct {
	ListParams        `form:"*"`
	Created           *int64                         `form:"created"`
	CreatedRange      *RangeQueryParams              `form:"created"`
	Customer          *string                        `form:"customer"`
	IDs               []*string                      `form:"ids"`
	Status            *string                        `form:"status"`
	StatusTransitions *StatusTransitionsFilterParams `form:"status_transitions"`
	UpstreamIDs       []*string                      `form:"upstream_ids"`
}

// StatusTransitionsFilterParams are parameters that can used to filter on status_transition when listing orders.
type StatusTransitionsFilterParams struct {
	Canceled       *int64            `form:"canceled"`
	CanceledRange  *RangeQueryParams `form:"canceled"`
	Fulfilled      *int64            `form:"fulfilled"`
	FulfilledRange *RangeQueryParams `form:"fulfilled"`
	Paid           *int64            `form:"paid"`
	PaidRange      *RangeQueryParams `form:"paid"`
	Returned       *int64            `form:"returned"`
	ReturnedRange  *RangeQueryParams `form:"returned"`
}

// StatusTransitions are the timestamps at which the order status was updated.
type StatusTransitions struct {
	Canceled  int64 `json:"canceled"`
	Fulfilled int64 `json:"fulfiled"`
	Paid      int64 `json:"paid"`
	Returned  int64 `json:"returned"`
}

// OrderPayParams is the set of parameters that can be used when paying orders.
type OrderPayParams struct {
	Params         `form:"*"`
	ApplicationFee *int64        `form:"application_fee"`
	Customer       *string       `form:"customer"`
	Email          *string       `form:"email"`
	Source         *SourceParams `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
}

// OrderItemParams is the set of parameters describing an order item on order creation or update.
type OrderItemParams struct {
	Amount      *int64  `form:"amount"`
	Currency    *string `form:"currency"`
	Description *string `form:"description"`
	Parent      *string `form:"parent"`
	Quantity    *int64  `form:"quantity"`
	Type        *string `form:"type"`
}

// OrderItem is the resource representing an order item.
type OrderItem struct {
	Amount      int64            `json:"amount"`
	Currency    Currency         `json:"currency"`
	Description string           `json:"description"`
	Parent      *OrderItemParent `json:"parent"`
	Quantity    int64            `json:"quantity"`
	Type        OrderItemType    `json:"type"`
}

// SetSource adds valid sources to a OrderParams object,
// returning an error for unsupported sources.
func (op *OrderPayParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	op.Source = source
	return err
}

// UnmarshalJSON handles deserialization of an OrderItemParent.
// This custom unmarshaling is needed because the resulting
// property may be an id or a full SKU struct if it was expanded.
func (p *OrderItemParent) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		p.ID = id
		return nil
	}

	type orderItemParent OrderItemParent
	var v orderItemParent
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	var err error
	*p = OrderItemParent(v)

	switch p.Type {
	case OrderItemParentTypeSKU:
		// Currently only SKUs `parent` is returned as an object when expanded.
		// For other items only IDs are returned.
		if err = json.Unmarshal(data, &p.SKU); err != nil {
			return err
		}
		p.ID = p.SKU.ID
	}

	return nil
}

// UnmarshalJSON handles deserialization of an Order.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (o *Order) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		o.ID = id
		return nil
	}

	type order Order
	var v order
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*o = Order(v)
	return nil
}
<commit_msg>JSON spelling error<commit_after>package stripe

import (
	"encoding/json"
)

// OrderStatus represents the statuses of an order object.
type OrderStatus string

// List of values that OrderStatus can take.
const (
	OrderStatusCanceled  OrderStatus = "canceled"
	OrderStatusCreated   OrderStatus = "created"
	OrderStatusFulfilled OrderStatus = "fulfilled"
	OrderStatusPaid      OrderStatus = "paid"
	OrderStatusReturned  OrderStatus = "returned"
)

// OrderDeliveryEstimateType represents the type of delivery estimate for shipping methods
type OrderDeliveryEstimateType string

// List of values that OrderDeliveryEstimateType can take.
const (
	OrderDeliveryEstimateTypeExact OrderDeliveryEstimateType = "exact"
	OrderDeliveryEstimateTypeRange OrderDeliveryEstimateType = "range"
)

// OrderItemType represents the type of order item
type OrderItemType string

// List of values that OrderItemType can take.
const (
	OrderItemTypeCoupon   OrderItemType = "coupon"
	OrderItemTypeDiscount OrderItemType = "discount"
	OrderItemTypeSKU      OrderItemType = "sku"
	OrderItemTypeShipping OrderItemType = "shipping"
	OrderItemTypeTax      OrderItemType = "tax"
)

// OrderItemParentType represents the type of order item parent
type OrderItemParentType string

// List of values that OrderItemParentType can take.
const (
	OrderItemParentTypeCoupon   OrderItemParentType = "coupon"
	OrderItemParentTypeDiscount OrderItemParentType = "discount"
	OrderItemParentTypeSKU      OrderItemParentType = "sku"
	OrderItemParentTypeShipping OrderItemParentType = "shipping"
	OrderItemParentTypeTax      OrderItemParentType = "tax"
)

// OrderItemParent describes the parent of an order item.
type OrderItemParent struct {
	ID   string              `json:"id"`
	SKU  *SKU                `json:"-"`
	Type OrderItemParentType `json:"object"`
}

// OrderParams is the set of parameters that can be used when creating an order.
type OrderParams struct {
	Params   `form:"*"`
	Coupon   *string            `form:"coupon"`
	Currency *string            `form:"currency"`
	Customer *string            `form:"customer"`
	Email    *string            `form:"email"`
	Items    []*OrderItemParams `form:"items"`
	Shipping *ShippingParams    `form:"shipping"`
}

// ShippingParams is the set of parameters that can be used for the shipping hash
// on order creation.
type ShippingParams struct {
	Address *AddressParams `form:"address"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// OrderUpdateParams is the set of parameters that can be used when updating an order.
type OrderUpdateParams struct {
	Params                 `form:"*"`
	Coupon                 *string                    `form:"coupon"`
	SelectedShippingMethod *string                    `form:"selected_shipping_method"`
	Shipping               *OrderUpdateShippingParams `form:"shipping"`
	Status                 *string                    `form:"status"`
}

// OrderUpdateShippingParams is the set of parameters that can be used for the shipping
// hash on order update.
type OrderUpdateShippingParams struct {
	Carrier        *string `form:"carrier"`
	TrackingNumber *string `form:"tracking_number"`
}

// Shipping describes the shipping hash on an order.
type Shipping struct {
	Address        *Address `json:"address"`
	Carrier        string   `json:"carrier"`
	Name           string   `json:"name"`
	Phone          string   `json:"phone"`
	TrackingNumber string   `json:"tracking_number"`
}

// ShippingMethod describes a shipping method as available on an order.
type ShippingMethod struct {
	Amount           int64             `json:"amount"`
	ID               string            `json:"id"`
	Currency         Currency          `json:"currency"`
	DeliveryEstimate *DeliveryEstimate `json:"delivery_estimate"`
	Description      string            `json:"description"`
}

// DeliveryEstimate represent the properties available for a shipping method's
// estimated delivery.
type DeliveryEstimate struct {
	// If Type == Exact
	Date string `json:"date"`
	// If Type == Range
	Earliest string                    `json:"earliest"`
	Latest   string                    `json:"latest"`
	Type     OrderDeliveryEstimateType `json:"type"`
}

// Order is the resource representing a Stripe charge.
// For more details see https://stripe.com/docs/api#orders.
type Order struct {
	Amount                 int64             `json:"amount"`
	AmountReturned         int64             `json:"amount_returned"`
	Application            string            `json:"application"`
	ApplicationFee         int64             `json:"application_fee"`
	Charge                 *Charge           `json:"charge"`
	Created                int64             `json:"created"`
	Currency               Currency          `json:"currency"`
	Customer               Customer          `json:"customer"`
	Email                  string            `json:"email"`
	ID                     string            `json:"id"`
	Items                  []*OrderItem      `json:"items"`
	Livemode               bool              `json:"livemode"`
	Metadata               map[string]string `json:"metadata"`
	Returns                *OrderReturnList  `json:"returns"`
	SelectedShippingMethod *string           `json:"selected_shipping_method"`
	Shipping               *Shipping         `json:"shipping"`
	ShippingMethods        []*ShippingMethod `json:"shipping_methods"`
	Status                 string            `json:"status"`
	StatusTransitions      StatusTransitions `json:"status_transitions"`
	Updated                int64             `json:"updated"`
	UpstreamID             string            `json:"upstream_id"`
}

// OrderList is a list of orders as retrieved from a list endpoint.
type OrderList struct {
	ListMeta
	Data []*Order `json:"data"`
}

// OrderListParams is the set of parameters that can be used when listing orders.
type OrderListParams struct {
	ListParams        `form:"*"`
	Created           *int64                         `form:"created"`
	CreatedRange      *RangeQueryParams              `form:"created"`
	Customer          *string                        `form:"customer"`
	IDs               []*string                      `form:"ids"`
	Status            *string                        `form:"status"`
	StatusTransitions *StatusTransitionsFilterParams `form:"status_transitions"`
	UpstreamIDs       []*string                      `form:"upstream_ids"`
}

// StatusTransitionsFilterParams are parameters that can used to filter on status_transition when listing orders.
type StatusTransitionsFilterParams struct {
	Canceled       *int64            `form:"canceled"`
	CanceledRange  *RangeQueryParams `form:"canceled"`
	Fulfilled      *int64            `form:"fulfilled"`
	FulfilledRange *RangeQueryParams `form:"fulfilled"`
	Paid           *int64            `form:"paid"`
	PaidRange      *RangeQueryParams `form:"paid"`
	Returned       *int64            `form:"returned"`
	ReturnedRange  *RangeQueryParams `form:"returned"`
}

// StatusTransitions are the timestamps at which the order status was updated.
type StatusTransitions struct {
	Canceled  int64 `json:"canceled"`
	Fulfilled int64 `json:"fulfilled"`
	Paid      int64 `json:"paid"`
	Returned  int64 `json:"returned"`
}

// OrderPayParams is the set of parameters that can be used when paying orders.
type OrderPayParams struct {
	Params         `form:"*"`
	ApplicationFee *int64        `form:"application_fee"`
	Customer       *string       `form:"customer"`
	Email          *string       `form:"email"`
	Source         *SourceParams `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
}

// OrderItemParams is the set of parameters describing an order item on order creation or update.
type OrderItemParams struct {
	Amount      *int64  `form:"amount"`
	Currency    *string `form:"currency"`
	Description *string `form:"description"`
	Parent      *string `form:"parent"`
	Quantity    *int64  `form:"quantity"`
	Type        *string `form:"type"`
}

// OrderItem is the resource representing an order item.
type OrderItem struct {
	Amount      int64            `json:"amount"`
	Currency    Currency         `json:"currency"`
	Description string           `json:"description"`
	Parent      *OrderItemParent `json:"parent"`
	Quantity    int64            `json:"quantity"`
	Type        OrderItemType    `json:"type"`
}

// SetSource adds valid sources to a OrderParams object,
// returning an error for unsupported sources.
func (op *OrderPayParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	op.Source = source
	return err
}

// UnmarshalJSON handles deserialization of an OrderItemParent.
// This custom unmarshaling is needed because the resulting
// property may be an id or a full SKU struct if it was expanded.
func (p *OrderItemParent) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		p.ID = id
		return nil
	}

	type orderItemParent OrderItemParent
	var v orderItemParent
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	var err error
	*p = OrderItemParent(v)

	switch p.Type {
	case OrderItemParentTypeSKU:
		// Currently only SKUs `parent` is returned as an object when expanded.
		// For other items only IDs are returned.
		if err = json.Unmarshal(data, &p.SKU); err != nil {
			return err
		}
		p.ID = p.SKU.ID
	}

	return nil
}

// UnmarshalJSON handles deserialization of an Order.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (o *Order) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		o.ID = id
		return nil
	}

	type order Order
	var v order
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*o = Order(v)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018, Cossack Labs Limited

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 grpc_api implements gRPC API handler: users can send AcraStructs via gRPC to AcraConnector,
// AcraConnector wraps connection via Themis SecureSession. gRPC handler parses gRPC requests, decrypts AcraStructs
// and returns plaintext data via gRPC response.
package grpc_api

import (
	"errors"
	acrawriter "github.com/cossacklabs/acra/acra-writer"
	"github.com/cossacklabs/acra/cmd/acra-translator/common"
	"github.com/cossacklabs/acra/decryptor/base"
	"github.com/cossacklabs/acra/logging"
	"github.com/cossacklabs/acra/utils"
	"github.com/cossacklabs/themis/gothemis/keys"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

// DecryptGRPCService represents decryptor for decrypting AcraStructs from gRPC requests.
type DecryptGRPCService struct {
	*common.TranslatorData
}

// NewDecryptGRPCService creates new DecryptGRPCService.
func NewDecryptGRPCService(data *common.TranslatorData) (*DecryptGRPCService, error) {
	return &DecryptGRPCService{TranslatorData: data}, nil
}

// Errors possible during decrypting AcraStructs.
var (
	ErrCantDecrypt      = errors.New("can't decrypt data")
	ErrClientIDRequired = errors.New("clientID is empty")
	ErrCantEncrypt      = errors.New("can't encrypt data")
)

// Encrypt encrypt data from gRPC request and returns AcraStruct or error.
func (service *DecryptGRPCService) Encrypt(ctx context.Context, request *EncryptRequest) (*EncryptResponse, error) {
	timer := prometheus.NewTimer(prometheus.ObserverFunc(common.RequestProcessingTimeHistogram.WithLabelValues(common.GrpcRequestType).Observe))
	defer timer.ObserveDuration()

	logger := logrus.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "translator": "grpc"})
	var publicKey *keys.PublicKey
	var err error
	if len(request.ZoneId) != 0 {
		publicKey, err = service.TranslatorData.Keystorage.GetZonePublicKey(request.ZoneId)
		logger.Debugln("Loaded zoneID key for encryption")
	} else {
		publicKey, err = service.TranslatorData.Keystorage.GetClientIDEncryptionPublicKey(request.ClientId)
		logger.Debugln("Loaded clientID key for encryption")
	}
	if err != nil {
		base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc()
		msg := "Invalid client or zone id"
		logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantReadKeys).Warningln(msg)
		return nil, ErrCantEncrypt
	}
	// publicKey will be clientID' if wasn't provided ZoneID and context.ZoneID will be nil, otherwise used ZoneID
	// public key and context.ZoneID will have value
	acrastruct, err := acrawriter.CreateAcrastruct(request.Data, publicKey, request.ZoneId)
	if err != nil {
		base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc()
		msg := "Unexpected error with AcraStruct generation"
		logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantEncryptData).Warningln(msg)
		return nil, ErrCantEncrypt
	}
	base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc()
	logger.Infoln("Encrypted data to AcraStruct")
	return &EncryptResponse{Acrastruct: acrastruct}, nil
}

// Decrypt decrypts AcraStruct from gRPC request and returns decrypted data or error.
func (service *DecryptGRPCService) Decrypt(ctx context.Context, request *DecryptRequest) (*DecryptResponse, error) {
	var privateKeys []*keys.PrivateKey
	var err error
	var decryptionContext []byte

	timer := prometheus.NewTimer(prometheus.ObserverFunc(common.RequestProcessingTimeHistogram.WithLabelValues(common.GrpcRequestType).Observe))
	defer timer.ObserveDuration()

	logger := logrus.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "translator": "grpc"})
	if len(request.ClientId) == 0 {
		logrus.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorClientIDMissing).Errorln("GRPC request without ClientID not allowed")
		return nil, ErrClientIDRequired
	}
	if len(request.ZoneId) != 0 {
		privateKeys, err = service.TranslatorData.Keystorage.GetZonePrivateKeys(request.ZoneId)
		decryptionContext = request.ZoneId
	} else {
		privateKeys, err = service.TranslatorData.Keystorage.GetServerDecryptionPrivateKeys(request.ClientId)
	}
	if err != nil {
		base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc()
		logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantReadKeys).WithError(err).Errorln("Can't load private key for decryption")
		return nil, ErrCantDecrypt
	}
	data, decryptErr := base.DecryptRotatedAcrastruct(request.Acrastruct, privateKeys, decryptionContext)
	for _, privateKey := range privateKeys {
		utils.FillSlice(byte(0), privateKey.Value)
	}
	if decryptErr != nil {
		base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc()
		logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).WithError(decryptErr).Errorln("Can't decrypt AcraStruct")
		if service.TranslatorData.CheckPoisonRecords {
			poisoned, err := base.CheckPoisonRecord(request.Acrastruct, service.TranslatorData.Keystorage)
			if err != nil {
				logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantCheckPoisonRecord).WithError(err).Errorln("Can't check for poison record, possible missing Poison record decryption key")
				return nil, ErrCantDecrypt
			}
			if poisoned {
				logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorRecognizedPoisonRecord).Errorln("Recognized poison record")
				if service.TranslatorData.PoisonRecordCallbacks.HasCallbacks() {
					if err := service.TranslatorData.PoisonRecordCallbacks.Call(); err != nil {
						logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantHandleRecognizedPoisonRecord).WithError(err).Errorln("Unexpected error on poison record's callbacks")
					}
				}
				// don't show users that we found poison record
				return nil, ErrCantDecrypt
			}
		}
		return nil, ErrCantDecrypt
	}
	base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc()
	return &DecryptResponse{Data: data}, nil
}
<commit_msg>add logs for acra-translator<commit_after>/*
Copyright 2018, Cossack Labs Limited

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 grpc_api implements gRPC API handler: users can send AcraStructs via gRPC to AcraConnector,
// AcraConnector wraps connection via Themis SecureSession. gRPC handler parses gRPC requests, decrypts AcraStructs
// and returns plaintext data via gRPC response.
package grpc_api

import (
	"errors"
	acrawriter "github.com/cossacklabs/acra/acra-writer"
	"github.com/cossacklabs/acra/cmd/acra-translator/common"
	"github.com/cossacklabs/acra/decryptor/base"
	"github.com/cossacklabs/acra/logging"
	"github.com/cossacklabs/acra/utils"
	"github.com/cossacklabs/themis/gothemis/keys"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

// DecryptGRPCService represents decryptor for decrypting AcraStructs from gRPC requests.
type DecryptGRPCService struct {
	*common.TranslatorData
	logger *logrus.Entry
}

// NewDecryptGRPCService creates new DecryptGRPCService.
func NewDecryptGRPCService(data *common.TranslatorData) (*DecryptGRPCService, error) {
	return &DecryptGRPCService{TranslatorData: data, logger: logrus.WithField("service", "grpc_service")}, nil
}

// Errors possible during decrypting AcraStructs.
var (
	ErrCantDecrypt      = errors.New("can't decrypt data")
	ErrClientIDRequired = errors.New("clientID is empty")
	ErrCantEncrypt      = errors.New("can't encrypt data")
)

// Encrypt encrypt data from gRPC request and returns AcraStruct or error.
func (service *DecryptGRPCService) Encrypt(ctx context.Context, request *EncryptRequest) (*EncryptResponse, error) {
	service.logger.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "operation": "Encrypt"}).Debugln("New request")
	defer service.logger.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "operation": "Encrypt"}).Debugln("End processing request")
	timer := prometheus.NewTimer(prometheus.ObserverFunc(common.RequestProcessingTimeHistogram.WithLabelValues(common.GrpcRequestType).Observe))
	defer timer.ObserveDuration()

	logger := logrus.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "translator": "grpc"})
	var publicKey *keys.PublicKey
	var err error
	if len(request.ZoneId) != 0 {
		publicKey, err = service.TranslatorData.Keystorage.GetZonePublicKey(request.ZoneId)
		logger.Debugln("Loaded zoneID key for encryption")
	} else {
		publicKey, err = service.TranslatorData.Keystorage.GetClientIDEncryptionPublicKey(request.ClientId)
		logger.Debugln("Loaded clientID key for encryption")
	}
	if err != nil {
		base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc()
		msg := "Invalid client or zone id"
		logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantReadKeys).Warningln(msg)
		return nil, ErrCantEncrypt
	}
	// publicKey will be clientID' if wasn't provided ZoneID and context.ZoneID will be nil, otherwise used ZoneID
	// public key and context.ZoneID will have value
	acrastruct, err := acrawriter.CreateAcrastruct(request.Data, publicKey, request.ZoneId)
	if err != nil {
		base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc()
		msg := "Unexpected error with AcraStruct generation"
		logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantEncryptData).Warningln(msg)
		return nil, ErrCantEncrypt
	}
	base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc()
	logger.Infoln("Encrypted data to AcraStruct")
	return &EncryptResponse{Acrastruct: acrastruct}, nil
}

// Decrypt decrypts AcraStruct from gRPC request and returns decrypted data or error.
func (service *DecryptGRPCService) Decrypt(ctx context.Context, request *DecryptRequest) (*DecryptResponse, error) {
	service.logger.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "operation": "Decrypt"}).Debugln("New request")
	defer service.logger.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "operation": "Decrypt"}).Debugln("End processing request")
	var privateKeys []*keys.PrivateKey
	var err error
	var decryptionContext []byte

	timer := prometheus.NewTimer(prometheus.ObserverFunc(common.RequestProcessingTimeHistogram.WithLabelValues(common.GrpcRequestType).Observe))
	defer timer.ObserveDuration()

	logger := logrus.WithFields(logrus.Fields{"client_id": string(request.ClientId), "zone_id": string(request.ZoneId), "translator": "grpc"})
	if len(request.ClientId) == 0 {
		logrus.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorClientIDMissing).Errorln("GRPC request without ClientID not allowed")
		return nil, ErrClientIDRequired
	}
	if len(request.ZoneId) != 0 {
		privateKeys, err = service.TranslatorData.Keystorage.GetZonePrivateKeys(request.ZoneId)
		decryptionContext = request.ZoneId
	} else {
		privateKeys, err = service.TranslatorData.Keystorage.GetServerDecryptionPrivateKeys(request.ClientId)
	}
	if err != nil {
		base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc()
		logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantReadKeys).WithError(err).Errorln("Can't load private key for decryption")
		return nil, ErrCantDecrypt
	}
	data, decryptErr := base.DecryptRotatedAcrastruct(request.Acrastruct, privateKeys, decryptionContext)
	for _, privateKey := range privateKeys {
		utils.FillSlice(byte(0), privateKey.Value)
	}
	if decryptErr != nil {
		base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc()
		logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).WithError(decryptErr).Errorln("Can't decrypt AcraStruct")
		if service.TranslatorData.CheckPoisonRecords {
			poisoned, err := base.CheckPoisonRecord(request.Acrastruct, service.TranslatorData.Keystorage)
			if err != nil {
				logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantCheckPoisonRecord).WithError(err).Errorln("Can't check for poison record, possible missing Poison record decryption key")
				return nil, ErrCantDecrypt
			}
			if poisoned {
				logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorRecognizedPoisonRecord).Errorln("Recognized poison record")
				if service.TranslatorData.PoisonRecordCallbacks.HasCallbacks() {
					if err := service.TranslatorData.PoisonRecordCallbacks.Call(); err != nil {
						logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantHandleRecognizedPoisonRecord).WithError(err).Errorln("Unexpected error on poison record's callbacks")
					}
				}
				// don't show users that we found poison record
				return nil, ErrCantDecrypt
			}
		}
		return nil, ErrCantDecrypt
	}
	base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc()
	return &DecryptResponse{Data: data}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 build

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/kubernetes-incubator/apiserver-builder/cmd/apiserver-boot/boot/util"
	"github.com/spf13/cobra"
	"regexp"
)

var versionedAPIs []string
var unversionedAPIs []string
var copyright string = "boilerplate.go.txt"

var generateCmd = &cobra.Command{
	Use:   "generated",
	Short: "Run code generators against repo.",
	Long:  `Automatically run by most build commands.  Writes generated source code for a repo.`,
	Example: `# Run code generators.
apiserver-boot build generated`,
	Run: RunGenerate,
}

var genericAPI = strings.Join([]string{
	"k8s.io/client-go/pkg/api/v1",
	"k8s.io/client-go/pkg/apis/apps/v1beta1",
	"k8s.io/client-go/pkg/apis/authentication/v1",
	"k8s.io/client-go/pkg/apis/authentication/v1beta1",
	"k8s.io/client-go/pkg/apis/authorization/v1",
	"k8s.io/client-go/pkg/apis/authorization/v1beta1",
	"k8s.io/client-go/pkg/apis/autoscaling/v1",
	"k8s.io/client-go/pkg/apis/autoscaling/v2alpha1",
	"k8s.io/client-go/pkg/apis/batch/v1",
	"k8s.io/client-go/pkg/apis/batch/v2alpha1",
	"k8s.io/client-go/pkg/apis/certificates/v1beta1",
	"k8s.io/client-go/pkg/apis/extensions/v1beta1",
	"k8s.io/client-go/pkg/apis/policy/v1beta1",
	"k8s.io/client-go/pkg/apis/rbac/v1alpha1",
	"k8s.io/client-go/pkg/apis/rbac/v1beta1",
	"k8s.io/client-go/pkg/apis/settings/v1alpha1",
	"k8s.io/client-go/pkg/apis/storage/v1",
	"k8s.io/client-go/pkg/apis/storage/v1beta1",
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/api/resource",
	"k8s.io/apimachinery/pkg/version",
	"k8s.io/apimachinery/pkg/runtime",
	"k8s.io/apimachinery/pkg/util/intstr"}, ",")

var extraAPI = strings.Join([]string{
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/conversion",
	"k8s.io/apimachinery/pkg/runtime"}, ",")

func AddGenerate(cmd *cobra.Command) {
	cmd.AddCommand(generateCmd)
	generateCmd.Flags().StringArrayVar(&versionedAPIs, "api-versions", []string{}, "comma separated list of APIs Versions.  e.g. foo/v1beta1,bar/v1  defaults to all directories under pkd/apis/group/version")
	generateCmd.AddCommand(generateCleanCmd)
}

var generateCleanCmd = &cobra.Command{
	Use:   "clean",
	Short: "Removes generated source code",
	Long:  `Removes generated source code`,
	Run:   RunCleanGenerate,
}

func RunCleanGenerate(cmd *cobra.Command, args []string) {
	os.RemoveAll(filepath.Join("pkg", "client", "clientset_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "informers_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "listers_generated"))
	os.Remove(filepath.Join("pkg", "openapi", "openapi_generated.go"))

	filepath.Walk("pkg", func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() && strings.HasPrefix(info.Name(), "zz_generated.") {
			return os.Remove(path)
		}
		return nil
	})
}

func RunGenerate(cmd *cobra.Command, args []string) {
	initApis()

	util.GetCopyright(copyright)

	root, err := os.Executable()
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	root = filepath.Dir(root)

	all := []string{}
	versioned := []string{}
	for _, v := range versionedAPIs {
		v = filepath.Join(util.Repo, "pkg", "apis", v)
		versioned = append(versioned, "--input-dirs", v)
		all = append(all, "--input-dirs", v)
	}
	unversioned := []string{}
	for _, u := range unversionedAPIs {
		u = filepath.Join(util.Repo, "pkg", "apis", u)
		unversioned = append(unversioned, "--input-dirs", u)
		all = append(all, "--input-dirs", u)
	}

	c := exec.Command(filepath.Join(root, "apiregister-gen"),
		"--input-dirs", filepath.Join(util.Repo, "pkg", "apis", "..."),
		"--input-dirs", filepath.Join(util.Repo, "pkg", "controller", "..."),
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err := c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run apiregister-gen %s %v", out, err)
	}

	c = exec.Command(filepath.Join(root, "conversion-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"-O", "zz_generated.conversion",
			"--extra-peer-dirs", extraAPI)...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run conversion-gen %s %v", out, err)
	}

	c = exec.Command(filepath.Join(root, "deepcopy-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"-O", "zz_generated.deepcopy")...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run deepcopy-gen %s %v", out, err)
	}

	c = exec.Command(filepath.Join(root, "openapi-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"-i", genericAPI,
			"--output-package", filepath.Join(util.Repo, "pkg", "openapi"))...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run openapi-gen %s %v", out, err)
	}

	c = exec.Command(filepath.Join(root, "defaulter-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"-O", "zz_generated.defaults",
			"--extra-peer-dirs=", extraAPI)...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run defaulter-gen %s %v", out, err)
	}

	// Builder the versioned apis client
	clientPkg := filepath.Join(util.Repo, "pkg", "client")
	clientset := filepath.Join(clientPkg, "clientset_generated")
	c = exec.Command(filepath.Join(root, "client-gen"),
		"-o", util.GoSrc,
		"--go-header-file", copyright,
		"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
		"--input", strings.Join(versionedAPIs, ","),
		"--clientset-path", clientset,
		"--clientset-name", "clientset",
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run client-gen %s %v", out, err)
	}

	c = exec.Command(filepath.Join(root, "client-gen"),
		"-o", util.GoSrc,
		"--go-header-file", copyright,
		"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
		"--input", strings.Join(unversionedAPIs, ","),
		"--clientset-path", clientset,
		"--clientset-name", "internalclientset")
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run client-gen for unversioned APIs %s %v", out, err)
	}

	listerPkg := filepath.Join(clientPkg, "listers_generated")
	c = exec.Command(filepath.Join(root, "lister-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--output-package", listerPkg)...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run lister-gen %s %v", out, err)
	}

	informerPkg := filepath.Join(clientPkg, "informers_generated")
	c = exec.Command(filepath.Join(root, "informer-gen"),
		append(all,
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--output-package", informerPkg,
			"--listers-package", listerPkg,
			"--versioned-clientset-package", filepath.Join(clientset, "clientset"),
			"--internal-clientset-package", filepath.Join(clientset, "internalclientset"))...,
	)
	fmt.Printf("%s\n", strings.Join(c.Args, " "))
	out, err = c.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run informer-gen %s %v", out, err)
	}
}

func initApis() {
	if len(versionedAPIs) == 0 {
		groups, err := ioutil.ReadDir(filepath.Join("pkg", "apis"))
		if err != nil {
			log.Fatalf("could not read pkg/apis directory to find api Versions")
		}
		for _, g := range groups {
			if g.IsDir() {
				versionFiles, err := ioutil.ReadDir(filepath.Join("pkg", "apis", g.Name()))
				if err != nil {
					log.Fatalf("could not read pkg/apis/%s directory to find api Versions", g.Name())
				}
				versionMatch := regexp.MustCompile("^v\\d+(alpha\\d+|beta\\d+)*$")
				for _, v := range versionFiles {
					if v.IsDir() && versionMatch.MatchString(v.Name()) {
						versionedAPIs = append(versionedAPIs, filepath.Join(g.Name(), v.Name()))
					}
				}
			}
		}
	}
	u := map[string]bool{}
	for _, a := range versionedAPIs {
		u[path.Dir(a)] = true
	}
	for a, _ := range u {
		unversionedAPIs = append(unversionedAPIs, a)
	}
}
<commit_msg>Support for skipping specific code generators.<commit_after>/*
Copyright 2017 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 build

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/spf13/cobra"

	"github.com/kubernetes-incubator/apiserver-builder/cmd/apiserver-boot/boot/util"
)

var versionedAPIs []string
var unversionedAPIs []string
var copyright string = "boilerplate.go.txt"
var skipGenerators []string

var generateCmd = &cobra.Command{
	Use:   "generated",
	Short: "Run code generators against repo.",
	Long:  `Automatically run by most build commands.  Writes generated source code for a repo.`,
	Example: `# Run code generators.
apiserver-boot build generated`,
	Run: RunGenerate,
}

var genericAPI = strings.Join([]string{
	"k8s.io/client-go/pkg/api/v1",
	"k8s.io/client-go/pkg/apis/apps/v1beta1",
	"k8s.io/client-go/pkg/apis/authentication/v1",
	"k8s.io/client-go/pkg/apis/authentication/v1beta1",
	"k8s.io/client-go/pkg/apis/authorization/v1",
	"k8s.io/client-go/pkg/apis/authorization/v1beta1",
	"k8s.io/client-go/pkg/apis/autoscaling/v1",
	"k8s.io/client-go/pkg/apis/autoscaling/v2alpha1",
	"k8s.io/client-go/pkg/apis/batch/v1",
	"k8s.io/client-go/pkg/apis/batch/v2alpha1",
	"k8s.io/client-go/pkg/apis/certificates/v1beta1",
	"k8s.io/client-go/pkg/apis/extensions/v1beta1",
	"k8s.io/client-go/pkg/apis/policy/v1beta1",
	"k8s.io/client-go/pkg/apis/rbac/v1alpha1",
	"k8s.io/client-go/pkg/apis/rbac/v1beta1",
	"k8s.io/client-go/pkg/apis/settings/v1alpha1",
	"k8s.io/client-go/pkg/apis/storage/v1",
	"k8s.io/client-go/pkg/apis/storage/v1beta1",
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/api/resource",
	"k8s.io/apimachinery/pkg/version",
	"k8s.io/apimachinery/pkg/runtime",
	"k8s.io/apimachinery/pkg/util/intstr"}, ",")

var extraAPI = strings.Join([]string{
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/conversion",
	"k8s.io/apimachinery/pkg/runtime"}, ",")

func AddGenerate(cmd *cobra.Command) {
	cmd.AddCommand(generateCmd)
	generateCmd.Flags().StringArrayVar(&versionedAPIs, "api-versions", []string{}, "comma separated list of APIs Versions.  e.g. foo/v1beta1,bar/v1  defaults to all directories under pkd/apis/group/version")
	generateCmd.Flags().StringArrayVar(&skipGenerators, "skip-generators", []string{}, "List of generators to skip.  If using apiserver-boot on a repo that does not use the apiserver-build code generation, specify --skip-generators=apiregister-gen")
	generateCmd.AddCommand(generateCleanCmd)
}

var generateCleanCmd = &cobra.Command{
	Use:   "clean",
	Short: "Removes generated source code",
	Long:  `Removes generated source code`,
	Run:   RunCleanGenerate,
}

func RunCleanGenerate(cmd *cobra.Command, args []string) {
	os.RemoveAll(filepath.Join("pkg", "client", "clientset_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "informers_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "listers_generated"))
	os.Remove(filepath.Join("pkg", "openapi", "openapi_generated.go"))

	filepath.Walk("pkg", func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() && strings.HasPrefix(info.Name(), "zz_generated.") {
			return os.Remove(path)
		}
		return nil
	})
}

func RunGenerate(cmd *cobra.Command, args []string) {
	initApis()

	skip := map[string]interface{}{}
	for _, s := range skipGenerators {
		skip[s] = nil
	}

	if _, f1 := skip["client-gen"]; f1 {
		if _, f2 := skip["lister-gen"]; !f2 {
			log.Fatalf("Must skip lister-gen if client-gen is skipped")
		}
	}

	if _, f2 := skip["lister-gen"]; f2 {
		if _, f3 := skip["informer-gen"]; !f3 {
			log.Fatalf("Must skip informer-gen if lister-gen is skipped")
		}
	}

	util.GetCopyright(copyright)

	root, err := os.Executable()
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	root = filepath.Dir(root)

	all := []string{}
	versioned := []string{}
	for _, v := range versionedAPIs {
		v = filepath.Join(util.Repo, "pkg", "apis", v)
		versioned = append(versioned, "--input-dirs", v)
		all = append(all, "--input-dirs", v)
	}
	unversioned := []string{}
	for _, u := range unversionedAPIs {
		u = filepath.Join(util.Repo, "pkg", "apis", u)
		unversioned = append(unversioned, "--input-dirs", u)
		all = append(all, "--input-dirs", u)
	}

	if _, f := skip["apiregister-gen"]; !f {
		c := exec.Command(filepath.Join(root, "apiregister-gen"),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "apis", "..."),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "controller", "..."),
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run apiregister-gen %s %v", out, err)
		}
	}

	if _, f := skip["conversion-gen"]; !f {
		c := exec.Command(filepath.Join(root, "conversion-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.conversion",
				"--extra-peer-dirs", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run conversion-gen %s %v", out, err)
		}
	}

	if _, f := skip["deepcopy-gen"]; !f {
		c := exec.Command(filepath.Join(root, "deepcopy-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.deepcopy")...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run deepcopy-gen %s %v", out, err)
		}
	}

	if _, f := skip["openapi-gen"]; !f {
		c := exec.Command(filepath.Join(root, "openapi-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-i", genericAPI,
				"--output-package", filepath.Join(util.Repo, "pkg", "openapi"))...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run openapi-gen %s %v", out, err)
		}
	}

	if _, f := skip["defaulter-gen"]; !f {
		c := exec.Command(filepath.Join(root, "defaulter-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.defaults",
				"--extra-peer-dirs=", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run defaulter-gen %s %v", out, err)
		}
	}

	if _, f := skip["client-gen"]; !f {
		// Builder the versioned apis client
		clientPkg := filepath.Join(util.Repo, "pkg", "client")
		clientset := filepath.Join(clientPkg, "clientset_generated")
		c := exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(versionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "clientset",
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen %s %v", out, err)
		}

		c = exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(unversionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "internalclientset")
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err = c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen for unversioned APIs %s %v", out, err)
		}

		if _, f := skip["lister-gen"]; !f {
			listerPkg := filepath.Join(clientPkg, "listers_generated")
			c = exec.Command(filepath.Join(root, "lister-gen"),
				append(all,
					"-o", util.GoSrc,
					"--go-header-file", copyright,
					"--output-package", listerPkg)...,
			)
			fmt.Printf("%s\n", strings.Join(c.Args, " "))
			out, err = c.CombinedOutput()
			if err != nil {
				log.Fatalf("failed to run lister-gen %s %v", out, err)
			}

			if _, f := skip["informer-gen"]; !f {
				informerPkg := filepath.Join(clientPkg, "informers_generated")
				c = exec.Command(filepath.Join(root, "informer-gen"),
					append(all,
						"-o", util.GoSrc,
						"--go-header-file", copyright,
						"--output-package", informerPkg,
						"--listers-package", listerPkg,
						"--versioned-clientset-package", filepath.Join(clientset, "clientset"),
						"--internal-clientset-package", filepath.Join(clientset, "internalclientset"))...,
				)
				fmt.Printf("%s\n", strings.Join(c.Args, " "))
				out, err := c.CombinedOutput()
				if err != nil {
					log.Fatalf("failed to run informer-gen %s %v", out, err)
				}
			}
		}
	}
}

func initApis() {
	if len(versionedAPIs) == 0 {
		groups, err := ioutil.ReadDir(filepath.Join("pkg", "apis"))
		if err != nil {
			log.Fatalf("could not read pkg/apis directory to find api Versions")
		}
		for _, g := range groups {
			if g.IsDir() {
				versionFiles, err := ioutil.ReadDir(filepath.Join("pkg", "apis", g.Name()))
				if err != nil {
					log.Fatalf("could not read pkg/apis/%s directory to find api Versions", g.Name())
				}
				versionMatch := regexp.MustCompile("^v\\d+(alpha\\d+|beta\\d+)*$")
				for _, v := range versionFiles {
					if v.IsDir() && versionMatch.MatchString(v.Name()) {
						versionedAPIs = append(versionedAPIs, filepath.Join(g.Name(), v.Name()))
					}
				}
			}
		}
	}
	u := map[string]bool{}
	for _, a := range versionedAPIs {
		u[path.Dir(a)] = true
	}
	for a, _ := range u {
		unversionedAPIs = append(unversionedAPIs, a)
	}
}
<|endoftext|>