<commit_before>package terraform

import (
	"fmt"
	"log"
	"sort"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/lang"
	"github.com/hashicorp/terraform/states"
)

// GraphNodeReferenceable must be implemented by any node that represents
// a Terraform thing that can be referenced (resource, module, etc.).
//
// Even if the thing has no name, this should return an empty list. By
// implementing this and returning a non-nil result, you say that this CAN
// be referenced and other methods of referencing may still be possible (such
// as by path!)
type GraphNodeReferenceable interface {
	GraphNodeModulePath

	// ReferenceableAddrs returns a list of addresses through which this can be
	// referenced.
	ReferenceableAddrs() []addrs.Referenceable
}

// GraphNodeReferencer must be implemented by nodes that reference other
// Terraform items and therefore depend on them.
type GraphNodeReferencer interface {
	GraphNodeModulePath

	// References returns a list of references made by this node, which
	// include both a referenced address and source location information for
	// the reference.
	References() []*addrs.Reference
}

type GraphNodeAttachDependencies interface {
	GraphNodeConfigResource
	AttachDependencies([]addrs.ConfigResource)
}

// GraphNodeReferenceOutside is an interface that can optionally be implemented.
// A node that implements it can specify that its own referenceable addresses
// and/or the addresses it references are in a different module than the
// node itself.
//
// Any referenceable addresses returned by ReferenceableAddrs are interpreted
// relative to the returned selfPath.
//
// Any references returned by References are interpreted relative to the
// returned referencePath.
//
// It is valid but not required for either of these paths to match what is
// returned by method Path, though if both match the main Path then there
// is no reason to implement this method.
//
// The primary use-case for this is the nodes representing module input
// variables, since their expressions are resolved in terms of their calling
// module, but they are still referenced from their own module.
type GraphNodeReferenceOutside interface {
	// ReferenceOutside returns a path in which any references from this node
	// are resolved.
	ReferenceOutside() (selfPath, referencePath addrs.Module)
}

// ReferenceTransformer is a GraphTransformer that connects all the
// nodes that reference each other in order to form the proper ordering.
type ReferenceTransformer struct{}

func (t *ReferenceTransformer) Transform(g *Graph) error {
	// Build a reference map so we can efficiently look up the references
	vs := g.Vertices()
	m := NewReferenceMap(vs)

	// Find the things that reference things and connect them
	for _, v := range vs {
		if _, ok := v.(GraphNodeDestroyer); ok {
			// destroy nodes references are not connected, since they can only
			// use their own state.
			continue
		}
		parents := m.References(v)
		parentsDbg := make([]string, len(parents))
		for i, v := range parents {
			parentsDbg[i] = dag.VertexName(v)
		}
		log.Printf(
			"[DEBUG] ReferenceTransformer: %q references: %v",
			dag.VertexName(v), parentsDbg)

		for _, parent := range parents {
			g.Connect(dag.BasicEdge(v, parent))
		}

		if len(parents) > 0 {
			continue
		}
	}

	return nil
}

// AttachDependenciesTransformer records all resource dependencies for each
// instance, and attaches the addresses to the node itself. Managed resource
// will record these in the state for proper ordering of destroy operations.
type AttachDependenciesTransformer struct {
	Config  *configs.Config
	State   *states.State
	Schemas *Schemas
}

func (t AttachDependenciesTransformer) Transform(g *Graph) error {
	// FIXME: this is only working with ResourceConfigAddr for now

	for _, v := range g.Vertices() {
		attacher, ok := v.(GraphNodeAttachDependencies)
		if !ok {
			continue
		}
		selfAddr := attacher.ResourceAddr()

		// Data sources don't need to track destroy dependencies
		if selfAddr.Resource.Mode == addrs.DataResourceMode {
			continue
		}

		ans, err := g.Ancestors(v)
		if err != nil {
			return err
		}

		// dedupe addrs when there's multiple instances involved, or
		// multiple paths in the un-reduced graph
		depMap := map[string]addrs.ConfigResource{}
		for _, d := range ans {
			var addr addrs.ConfigResource

			switch d := d.(type) {
			case GraphNodeResourceInstance:
				instAddr := d.ResourceInstanceAddr()
				addr = instAddr.ContainingResource().Config()
			case GraphNodeConfigResource:
				addr = d.ResourceAddr()
			default:
				continue
			}

			// Data sources don't need to track destroy dependencies
			if addr.Resource.Mode == addrs.DataResourceMode {
				continue
			}

			if addr.Equal(selfAddr) {
				continue
			}
			depMap[addr.String()] = addr
		}

		deps := make([]addrs.ConfigResource, 0, len(depMap))
		for _, d := range depMap {
			deps = append(deps, d)
		}
		sort.Slice(deps, func(i, j int) bool {
			return deps[i].String() < deps[j].String()
		})

		log.Printf("[TRACE] AttachDependenciesTransformer: %s depends on %s", attacher.ResourceAddr(), deps)
		attacher.AttachDependencies(deps)
	}

	return nil
}

// PruneUnusedValuesTransformer is a GraphTransformer that removes local,
// variable, and output values which are not referenced in the graph. If these
// values reference a resource that is no longer in the state the interpolation
// could fail.
type PruneUnusedValuesTransformer struct {
	Destroy bool
}

// FIXME: outputs will now  have module expansion root nodes dependent on them,
// so we need to filter those out too. Might be a good time to decouple this
// from concrete types, and implement some interfaces.
func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
	// Pruning a value can effect previously checked edges, so loop until there
	// are no more changes.
	for removed := 0; ; removed = 0 {
		for _, v := range g.Vertices() {
			switch v := v.(type) {
			case *NodeApplyableOutput:
				// If we're not certain this is a full destroy, we need to keep any
				// root module outputs
				if v.Addr.Module.IsRoot() && !t.Destroy {
					continue
				}
			case *NodePlannableOutput:
				// Have similar guardrails for plannable outputs as applyable above
				if v.Module.IsRoot() && !t.Destroy {
					continue
				}
			case *NodeLocal, *NodeApplyableModuleVariable, *NodePlannableModuleVariable:
				// OK
			default:
				// We're only concerned with variables, locals and outputs
				continue
			}

			dependants := g.UpEdges(v)

			switch dependants.Len() {
			case 0:
				// nothing at all depends on this
				log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
				g.Remove(v)
				removed++
			case 1:
				// because an output's destroy node always depends on the output,
				// we need to check for the case of a single destroy node.
				d := dependants.List()[0]
				if _, ok := d.(*NodeDestroyableOutput); ok {
					log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
					g.Remove(v)
					removed++
				}
			}
		}
		if removed == 0 {
			break
		}
	}

	return nil
}

// ReferenceMap is a structure that can be used to efficiently check
// for references on a graph.
type ReferenceMap struct {
	// vertices is a map from internal reference keys (as produced by the
	// mapKey method) to one or more vertices that are identified by each key.
	//
	// A particular reference key might actually identify multiple vertices,
	// e.g. in situations where one object is contained inside another.
	vertices map[string][]dag.Vertex
}

// References returns the set of vertices that the given vertex refers to,
// and any referenced addresses that do not have corresponding vertices.
func (m *ReferenceMap) References(v dag.Vertex) []dag.Vertex {
	rn, ok := v.(GraphNodeReferencer)
	if !ok {
		return nil
	}

	var matches []dag.Vertex

	for _, ref := range rn.References() {
		subject := ref.Subject

		key := m.referenceMapKey(v, subject)
		if _, exists := m.vertices[key]; !exists {
			// If what we were looking for was a ResourceInstance then we
			// might be in a resource-oriented graph rather than an
			// instance-oriented graph, and so we'll see if we have the
			// resource itself instead.
			switch ri := subject.(type) {
			case addrs.ResourceInstance:
				subject = ri.ContainingResource()
			case addrs.ResourceInstancePhase:
				subject = ri.ContainingResource()
			}
			key = m.referenceMapKey(v, subject)
		}
		vertices := m.vertices[key]
		for _, rv := range vertices {
			// don't include self-references
			if rv == v {
				continue
			}
			matches = append(matches, rv)
		}
	}

	return matches
}

func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string {
	return fmt.Sprintf("%s|%s", path.String(), addr.String())
}

// vertexReferenceablePath returns the path in which the given vertex can be
// referenced. This is the path that its results from ReferenceableAddrs
// are considered to be relative to.
//
// Only GraphNodeModulePath implementations can be referenced, so this method will
// panic if the given vertex does not implement that interface.
func vertexReferenceablePath(v dag.Vertex) addrs.Module {
	sp, ok := v.(GraphNodeModulePath)
	if !ok {
		// Only nodes with paths can participate in a reference map.
		panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp))
	}

	if outside, ok := v.(GraphNodeReferenceOutside); ok {
		// Vertex is referenced from a different module than where it was
		// declared.
		path, _ := outside.ReferenceOutside()
		return path
	}

	// Vertex is referenced from the same module as where it was declared.
	return sp.ModulePath()
}

// vertexReferencePath returns the path in which references _from_ the given
// vertex must be interpreted.
//
// Only GraphNodeModulePath implementations can have references, so this method
// will panic if the given vertex does not implement that interface.
func vertexReferencePath(v dag.Vertex) addrs.Module {
	sp, ok := v.(GraphNodeModulePath)
	if !ok {
		// Only nodes with paths can participate in a reference map.
		panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v))
	}

	if outside, ok := v.(GraphNodeReferenceOutside); ok {
		// Vertex makes references to objects in a different module than where
		// it was declared.
		_, path := outside.ReferenceOutside()
		return path
	}

	// Vertex makes references to objects in the same module as where it
	// was declared.
	return sp.ModulePath()
}

// referenceMapKey produces keys for the "edges" map. "referrer" is the vertex
// that the reference is from, and "addr" is the address of the object being
// referenced.
//
// The result is an opaque string that includes both the address of the given
// object and the address of the module instance that object belongs to.
//
// Only GraphNodeModulePath implementations can be referrers, so this method will
// panic if the given vertex does not implement that interface.
func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string {
	path := vertexReferencePath(referrer)
	return m.mapKey(path, addr)
}

// NewReferenceMap is used to create a new reference map for the
// given set of vertices.
func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
	var m ReferenceMap

	// Build the lookup table
	vertices := make(map[string][]dag.Vertex)
	for _, v := range vs {
		// We're only looking for referenceable nodes
		rn, ok := v.(GraphNodeReferenceable)
		if !ok {
			continue
		}

		path := vertexReferenceablePath(v)

		// Go through and cache them
		for _, addr := range rn.ReferenceableAddrs() {
			key := m.mapKey(path, addr)
			vertices[key] = append(vertices[key], v)
		}

		// Any node can be referenced by the address of the module it belongs
		// to or any of that module's ancestors.
		for _, addr := range path.Ancestors()[1:] {
			// Can be referenced either as the specific call instance (with
			// an instance key) or as the bare module call itself (the "module"
			// block in the parent module that created the instance).
			callPath, call := addr.Call()
			callKey := m.mapKey(callPath, call)
			vertices[callKey] = append(vertices[callKey], v)
		}
	}

	m.vertices = vertices
	return &m
}

// ReferencesFromConfig returns the references that a configuration has
// based on the interpolated variables in a configuration.
func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference {
	if body == nil {
		return nil
	}
	refs, _ := lang.ReferencesInBlock(body, schema)
	return refs
}

// appendResourceDestroyReferences identifies resource and resource instance
// references in the given slice and appends to it the "destroy-phase"
// equivalents of those references, returning the result.
//
// This can be used in the References implementation for a node which must also
// depend on the destruction of anything it references.
func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference {
	given := refs
	for _, ref := range given {
		switch tr := ref.Subject.(type) {
		case addrs.Resource:
			newRef := *ref // shallow copy
			newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
			refs = append(refs, &newRef)
		case addrs.ResourceInstance:
			newRef := *ref // shallow copy
			newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
			refs = append(refs, &newRef)
		}
		// FIXME: Using this method in module expansion references,
		// May want to refactor this method beyond resources
	}
	return refs
}

func modulePrefixStr(p addrs.ModuleInstance) string {
	return p.String()
}

func modulePrefixList(result []string, prefix string) []string {
	if prefix != "" {
		for i, v := range result {
			result[i] = fmt.Sprintf("%s.%s", prefix, v)
		}
	}

	return result
}
<commit_msg>check for the correct types when pruning values<commit_after>package terraform

import (
	"fmt"
	"log"
	"sort"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/lang"
	"github.com/hashicorp/terraform/states"
)

// GraphNodeReferenceable must be implemented by any node that represents
// a Terraform thing that can be referenced (resource, module, etc.).
//
// Even if the thing has no name, this should return an empty list. By
// implementing this and returning a non-nil result, you say that this CAN
// be referenced and other methods of referencing may still be possible (such
// as by path!)
type GraphNodeReferenceable interface {
	GraphNodeModulePath

	// ReferenceableAddrs returns a list of addresses through which this can be
	// referenced.
	ReferenceableAddrs() []addrs.Referenceable
}

// GraphNodeReferencer must be implemented by nodes that reference other
// Terraform items and therefore depend on them.
type GraphNodeReferencer interface {
	GraphNodeModulePath

	// References returns a list of references made by this node, which
	// include both a referenced address and source location information for
	// the reference.
	References() []*addrs.Reference
}

type GraphNodeAttachDependencies interface {
	GraphNodeConfigResource
	AttachDependencies([]addrs.ConfigResource)
}

// GraphNodeReferenceOutside is an interface that can optionally be implemented.
// A node that implements it can specify that its own referenceable addresses
// and/or the addresses it references are in a different module than the
// node itself.
//
// Any referenceable addresses returned by ReferenceableAddrs are interpreted
// relative to the returned selfPath.
//
// Any references returned by References are interpreted relative to the
// returned referencePath.
//
// It is valid but not required for either of these paths to match what is
// returned by method Path, though if both match the main Path then there
// is no reason to implement this method.
//
// The primary use-case for this is the nodes representing module input
// variables, since their expressions are resolved in terms of their calling
// module, but they are still referenced from their own module.
type GraphNodeReferenceOutside interface {
	// ReferenceOutside returns a path in which any references from this node
	// are resolved.
	ReferenceOutside() (selfPath, referencePath addrs.Module)
}

// ReferenceTransformer is a GraphTransformer that connects all the
// nodes that reference each other in order to form the proper ordering.
type ReferenceTransformer struct{}

func (t *ReferenceTransformer) Transform(g *Graph) error {
	// Build a reference map so we can efficiently look up the references
	vs := g.Vertices()
	m := NewReferenceMap(vs)

	// Find the things that reference things and connect them
	for _, v := range vs {
		if _, ok := v.(GraphNodeDestroyer); ok {
			// destroy nodes references are not connected, since they can only
			// use their own state.
			continue
		}
		parents := m.References(v)
		parentsDbg := make([]string, len(parents))
		for i, v := range parents {
			parentsDbg[i] = dag.VertexName(v)
		}
		log.Printf(
			"[DEBUG] ReferenceTransformer: %q references: %v",
			dag.VertexName(v), parentsDbg)

		for _, parent := range parents {
			g.Connect(dag.BasicEdge(v, parent))
		}

		if len(parents) > 0 {
			continue
		}
	}

	return nil
}

// AttachDependenciesTransformer records all resource dependencies for each
// instance, and attaches the addresses to the node itself. Managed resource
// will record these in the state for proper ordering of destroy operations.
type AttachDependenciesTransformer struct {
	Config  *configs.Config
	State   *states.State
	Schemas *Schemas
}

func (t AttachDependenciesTransformer) Transform(g *Graph) error {
	// FIXME: this is only working with ResourceConfigAddr for now

	for _, v := range g.Vertices() {
		attacher, ok := v.(GraphNodeAttachDependencies)
		if !ok {
			continue
		}
		selfAddr := attacher.ResourceAddr()

		// Data sources don't need to track destroy dependencies
		if selfAddr.Resource.Mode == addrs.DataResourceMode {
			continue
		}

		ans, err := g.Ancestors(v)
		if err != nil {
			return err
		}

		// dedupe addrs when there's multiple instances involved, or
		// multiple paths in the un-reduced graph
		depMap := map[string]addrs.ConfigResource{}
		for _, d := range ans {
			var addr addrs.ConfigResource

			switch d := d.(type) {
			case GraphNodeResourceInstance:
				instAddr := d.ResourceInstanceAddr()
				addr = instAddr.ContainingResource().Config()
			case GraphNodeConfigResource:
				addr = d.ResourceAddr()
			default:
				continue
			}

			// Data sources don't need to track destroy dependencies
			if addr.Resource.Mode == addrs.DataResourceMode {
				continue
			}

			if addr.Equal(selfAddr) {
				continue
			}
			depMap[addr.String()] = addr
		}

		deps := make([]addrs.ConfigResource, 0, len(depMap))
		for _, d := range depMap {
			deps = append(deps, d)
		}
		sort.Slice(deps, func(i, j int) bool {
			return deps[i].String() < deps[j].String()
		})

		log.Printf("[TRACE] AttachDependenciesTransformer: %s depends on %s", attacher.ResourceAddr(), deps)
		attacher.AttachDependencies(deps)
	}

	return nil
}

// PruneUnusedValuesTransformer is a GraphTransformer that removes local,
// variable, and output values which are not referenced in the graph. If these
// values reference a resource that is no longer in the state the interpolation
// could fail.
type PruneUnusedValuesTransformer struct {
	Destroy bool
}

// FIXME: outputs will now  have module expansion root nodes dependent on them,
// so we need to filter those out too. Might be a good time to decouple this
// from concrete types, and implement some interfaces.
func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
	// Pruning a value can effect previously checked edges, so loop until there
	// are no more changes.
	for removed := 0; ; removed = 0 {
		for _, v := range g.Vertices() {
			switch v := v.(type) {
			case *NodeApplyableOutput:
				// If we're not certain this is a full destroy, we need to keep any
				// root module outputs
				if v.Addr.Module.IsRoot() && !t.Destroy {
					continue
				}
			case *NodePlannableOutput:
				// Have similar guardrails for plannable outputs as applyable above
				if v.Module.IsRoot() && !t.Destroy {
					continue
				}
			case *NodeLocal, *NodeApplyableModuleVariable, *NodePlannableModuleVariable:
				// OK
			default:
				// We're only concerned with variables, locals and outputs
				continue
			}

			dependants := g.UpEdges(v)

			switch dependants.Len() {
			case 0:
				// nothing at all depends on this
				log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
				g.Remove(v)
				removed++
			default:
				// because an output's destroy node always depends on the output,
				// we need to check for the case of a single destroy node.
				removable := true
			SEARCH:
				for _, d := range dependants.List() {
					switch d.(type) {
					case *NodeDestroyableOutput, *nodeCloseModule:
						//pass
					default:
						removable = false
						break SEARCH
					}
				}

				if removable {
					log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
					g.Remove(v)
					removed++
				}
			}
		}
		if removed == 0 {
			break
		}
	}

	return nil
}

// ReferenceMap is a structure that can be used to efficiently check
// for references on a graph.
type ReferenceMap struct {
	// vertices is a map from internal reference keys (as produced by the
	// mapKey method) to one or more vertices that are identified by each key.
	//
	// A particular reference key might actually identify multiple vertices,
	// e.g. in situations where one object is contained inside another.
	vertices map[string][]dag.Vertex
}

// References returns the set of vertices that the given vertex refers to,
// and any referenced addresses that do not have corresponding vertices.
func (m *ReferenceMap) References(v dag.Vertex) []dag.Vertex {
	rn, ok := v.(GraphNodeReferencer)
	if !ok {
		return nil
	}

	var matches []dag.Vertex

	for _, ref := range rn.References() {
		subject := ref.Subject

		key := m.referenceMapKey(v, subject)
		if _, exists := m.vertices[key]; !exists {
			// If what we were looking for was a ResourceInstance then we
			// might be in a resource-oriented graph rather than an
			// instance-oriented graph, and so we'll see if we have the
			// resource itself instead.
			switch ri := subject.(type) {
			case addrs.ResourceInstance:
				subject = ri.ContainingResource()
			case addrs.ResourceInstancePhase:
				subject = ri.ContainingResource()
			}
			key = m.referenceMapKey(v, subject)
		}
		vertices := m.vertices[key]
		for _, rv := range vertices {
			// don't include self-references
			if rv == v {
				continue
			}
			matches = append(matches, rv)
		}
	}

	return matches
}

func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string {
	return fmt.Sprintf("%s|%s", path.String(), addr.String())
}

// vertexReferenceablePath returns the path in which the given vertex can be
// referenced. This is the path that its results from ReferenceableAddrs
// are considered to be relative to.
//
// Only GraphNodeModulePath implementations can be referenced, so this method will
// panic if the given vertex does not implement that interface.
func vertexReferenceablePath(v dag.Vertex) addrs.Module {
	sp, ok := v.(GraphNodeModulePath)
	if !ok {
		// Only nodes with paths can participate in a reference map.
		panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp))
	}

	if outside, ok := v.(GraphNodeReferenceOutside); ok {
		// Vertex is referenced from a different module than where it was
		// declared.
		path, _ := outside.ReferenceOutside()
		return path
	}

	// Vertex is referenced from the same module as where it was declared.
	return sp.ModulePath()
}

// vertexReferencePath returns the path in which references _from_ the given
// vertex must be interpreted.
//
// Only GraphNodeModulePath implementations can have references, so this method
// will panic if the given vertex does not implement that interface.
func vertexReferencePath(v dag.Vertex) addrs.Module {
	sp, ok := v.(GraphNodeModulePath)
	if !ok {
		// Only nodes with paths can participate in a reference map.
		panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v))
	}

	if outside, ok := v.(GraphNodeReferenceOutside); ok {
		// Vertex makes references to objects in a different module than where
		// it was declared.
		_, path := outside.ReferenceOutside()
		return path
	}

	// Vertex makes references to objects in the same module as where it
	// was declared.
	return sp.ModulePath()
}

// referenceMapKey produces keys for the "edges" map. "referrer" is the vertex
// that the reference is from, and "addr" is the address of the object being
// referenced.
//
// The result is an opaque string that includes both the address of the given
// object and the address of the module instance that object belongs to.
//
// Only GraphNodeModulePath implementations can be referrers, so this method will
// panic if the given vertex does not implement that interface.
func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string {
	path := vertexReferencePath(referrer)
	return m.mapKey(path, addr)
}

// NewReferenceMap is used to create a new reference map for the
// given set of vertices.
func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
	var m ReferenceMap

	// Build the lookup table
	vertices := make(map[string][]dag.Vertex)
	for _, v := range vs {
		// We're only looking for referenceable nodes
		rn, ok := v.(GraphNodeReferenceable)
		if !ok {
			continue
		}

		path := vertexReferenceablePath(v)

		// Go through and cache them
		for _, addr := range rn.ReferenceableAddrs() {
			key := m.mapKey(path, addr)
			vertices[key] = append(vertices[key], v)
		}

		// Any node can be referenced by the address of the module it belongs
		// to or any of that module's ancestors.
		for _, addr := range path.Ancestors()[1:] {
			// Can be referenced either as the specific call instance (with
			// an instance key) or as the bare module call itself (the "module"
			// block in the parent module that created the instance).
			callPath, call := addr.Call()
			callKey := m.mapKey(callPath, call)
			vertices[callKey] = append(vertices[callKey], v)
		}
	}

	m.vertices = vertices
	return &m
}

// ReferencesFromConfig returns the references that a configuration has
// based on the interpolated variables in a configuration.
func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference {
	if body == nil {
		return nil
	}
	refs, _ := lang.ReferencesInBlock(body, schema)
	return refs
}

// appendResourceDestroyReferences identifies resource and resource instance
// references in the given slice and appends to it the "destroy-phase"
// equivalents of those references, returning the result.
//
// This can be used in the References implementation for a node which must also
// depend on the destruction of anything it references.
func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference {
	given := refs
	for _, ref := range given {
		switch tr := ref.Subject.(type) {
		case addrs.Resource:
			newRef := *ref // shallow copy
			newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
			refs = append(refs, &newRef)
		case addrs.ResourceInstance:
			newRef := *ref // shallow copy
			newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy)
			refs = append(refs, &newRef)
		}
		// FIXME: Using this method in module expansion references,
		// May want to refactor this method beyond resources
	}
	return refs
}

func modulePrefixStr(p addrs.ModuleInstance) string {
	return p.String()
}

func modulePrefixList(result []string, prefix string) []string {
	if prefix != "" {
		for i, v := range result {
			result[i] = fmt.Sprintf("%s.%s", prefix, v)
		}
	}

	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"sort"
	"time"
)

//Song object
type song struct {
	Title  string
	Artist string
	Album  string
	Track  int
	ID     string
	path   string
}

//Types for sorting
type byTitle []*song
type byArtist []*song
type byAlbum []*song
type byLeast []*song
type byFavorite []*song

//
// Satisfy sort interfaces
//

func (songs byTitle) Len() int {
	return len(songs)
}

func (songs byArtist) Len() int {
	return len(songs)
}

func (songs byAlbum) Len() int {
	return len(songs)
}

func (songs byLeast) Len() int {
	return len(songs)
}

func (songs byFavorite) Len() int {
	return len(songs)
}

func (songs byTitle) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byArtist) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byAlbum) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byLeast) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byFavorite) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byTitle) Less(i, j int) bool {
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	return false
}

func (songs byArtist) Less(i, j int) bool {
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byAlbum) Less(i, j int) bool {
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byLeast) Less(i, j int) bool {
	var icount, jcount int64
	if v, ok := playcount[songs[i].ID]; ok {
		icount = v
	} else {
		icount = 0
	}
	if v, ok := playcount[songs[j].ID]; ok {
		jcount = v
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount < jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byFavorite) Less(i, j int) bool {
	var icount, jcount int
	if _, ok := liked[songs[i].ID]; ok {
		icount = 1
	} else {
		icount = 0
	}
	if _, ok := liked[songs[j].ID]; ok {
		jcount = 1
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount > jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

// Play counting endpoint
func countPlay(playcount map[string]int64) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		id := r.URL.Query().Get("id")
		apiMutex.Lock()
		playcount[id]++
		savePlayCount(playcount)
		apiMutex.Unlock()
	}
}

// Get likes or set or remove likes
func likes(likes map[string]bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		apiMutex.Lock()
		defer apiMutex.Unlock()
		if id := r.URL.Query().Get("id"); id != "" {
			if v, ok := likes[id]; ok {
				json.NewEncoder(w).Encode(v)
			} else {
				json.NewEncoder(w).Encode(false)
			}
			return
		}
		if like := r.URL.Query().Get("like"); like != "" {
			if _, ok := idcache[like]; !ok {
				return
			}
			if _, ok := likes[like]; ok {
				delete(likes, like)
				likedCount--
				json.NewEncoder(w).Encode(false)
			} else {
				likes[like] = true
				likedCount++
				json.NewEncoder(w).Encode(true)
			}
		}
		err := likedFile.Truncate(0)
		if err != nil {
			srvlog.Crit("Could not truncate likes file", "error", err)
			return
		}
		enc := json.NewEncoder(likedFile)
		enc.SetIndent("", " ")
		err = enc.Encode(likes)
		if err != nil {
			srvlog.Crit("Could not encode likes json", "error", err)
		}
	}
}

// Serves Audio files
func getStream(filemap map[string]string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		id := r.URL.Query().Get("id")
		if v, ok := filemap[id]; ok {
			_, err := os.Stat(v)
			if err != nil {
				srvlog.Crit("file missing", "file", v)
				w.WriteHeader(http.StatusNotFound)
				return
			}
			http.ServeFile(w, r, v)
		} else {
			w.WriteHeader(http.StatusNotFound)
			return
		}
	}
}

// Serves playlists
func getAPI(songs []*song) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		var err error
		switch r.URL.Query().Get("sort") {
		case "bytitle":
			if v, ok := sortcache["bytitle"]; !ok {
				apiMutex.Lock()
				sort.Sort(byTitle(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["bytitle"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by title failed", "error", err)
					delete(sortcache, "bytitle")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byartist":
			if v, ok := sortcache["byartist"]; !ok {
				apiMutex.Lock()
				sort.Sort(byArtist(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byartist"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by artist failed", "error", err)
					delete(sortcache, "byartist")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byalbum":
			if v, ok := sortcache["byalbum"]; !ok {
				apiMutex.Lock()
				sort.Sort(byAlbum(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byalbum"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by album failed", "error", err)
					delete(sortcache, "byalbum")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byshuffle":
			apiMutex.Lock()
			rand.Seed(time.Now().UnixNano())
			for i := len(songs) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byleast":
			apiMutex.Lock()
			sort.Sort(byLeast(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "bymost":
			apiMutex.Lock()
			sort.Sort(sort.Reverse(byLeast(songs)))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byfavorite":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:likedCount])
			apiMutex.Unlock()
		case "byfavshuffle":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			rand.Seed(time.Now().UnixNano())
			for i := len(songs[0:likedCount]) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:likedCount])
			apiMutex.Unlock()
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

// Serves song/album artwork
func artwork(w http.ResponseWriter, r *http.Request) {
	if err := authorize(w, r); err != nil {
		srvlog.Warn("Acess denied", "err", err)
		return
	}
	id := r.URL.Query().Get("id")
	reg := regexp.MustCompile("^\\w+$")
	if !reg.MatchString(id) {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file := fmt.Sprintf("./artcache/%s", id)
	_, err := os.Stat(file)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	http.ServeFile(w, r, file)
}

// Save playcount to a file
func savePlayCount(playcount map[string]int64) {
	playcountFile.Truncate(0)
	enc := json.NewEncoder(playcountFile)
	enc.SetIndent("", " ")
	err := enc.Encode(playcount)
	if err != nil {
		srvlog.Crit("could not encode playcount json", "error", err)
	}
}
<commit_msg>reduce code duplication<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"sort"
	"time"
)

//Song object
type song struct {
	Title  string
	Artist string
	Album  string
	Track  int
	ID     string
	path   string
}

type songs []*song

//Types for sorting
type byTitle []*song
type byArtist []*song
type byAlbum []*song
type byLeast []*song
type byFavorite []*song

//
// Satisfy sort interfaces
//

func (songs byTitle) Len() int {
	return len(songs)
}

func (songs byArtist) Len() int {
	return len(songs)
}

func (songs byAlbum) Len() int {
	return len(songs)
}

func (songs byLeast) Len() int {
	return len(songs)
}

func (songs byFavorite) Len() int {
	return len(songs)
}

func (songs byTitle) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byArtist) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byAlbum) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byLeast) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byFavorite) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byTitle) Less(i, j int) bool {
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	return false
}

func (songs byArtist) Less(i, j int) bool {
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byAlbum) Less(i, j int) bool {
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byLeast) Less(i, j int) bool {
	var icount, jcount int64
	if v, ok := playcount[songs[i].ID]; ok {
		icount = v
	} else {
		icount = 0
	}
	if v, ok := playcount[songs[j].ID]; ok {
		jcount = v
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount < jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byFavorite) Less(i, j int) bool {
	var icount, jcount int
	if _, ok := liked[songs[i].ID]; ok {
		icount = 1
	} else {
		icount = 0
	}
	if _, ok := liked[songs[j].ID]; ok {
		jcount = 1
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount > jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

// Play counting endpoint
func countPlay(playcount map[string]int64) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		id := r.URL.Query().Get("id")
		apiMutex.Lock()
		playcount[id]++
		savePlayCount(playcount)
		apiMutex.Unlock()
	}
}

// Get likes or set or remove likes
func likes(likes map[string]bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		apiMutex.Lock()
		defer apiMutex.Unlock()
		if id := r.URL.Query().Get("id"); id != "" {
			if v, ok := likes[id]; ok {
				json.NewEncoder(w).Encode(v)
			} else {
				json.NewEncoder(w).Encode(false)
			}
			return
		}
		if like := r.URL.Query().Get("like"); like != "" {
			if _, ok := idcache[like]; !ok {
				return
			}
			if _, ok := likes[like]; ok {
				delete(likes, like)
				likedCount--
				json.NewEncoder(w).Encode(false)
			} else {
				likes[like] = true
				likedCount++
				json.NewEncoder(w).Encode(true)
			}
		}
		err := likedFile.Truncate(0)
		if err != nil {
			srvlog.Crit("Could not truncate likes file", "error", err)
			return
		}
		enc := json.NewEncoder(likedFile)
		enc.SetIndent("", " ")
		err = enc.Encode(likes)
		if err != nil {
			srvlog.Crit("Could not encode likes json", "error", err)
		}
	}
}

// Serves Audio files
func getStream(filemap map[string]string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := authorize(w, r); err != nil {
			srvlog.Warn("Acess denied", "err", err)
			return
		}
		id := r.URL.Query().Get("id")
		if v, ok := filemap[id]; ok {
			_, err := os.Stat(v)
			if err != nil {
				srvlog.Crit("file missing", "file", v)
				w.WriteHeader(http.StatusNotFound)
				return
			}
			http.ServeFile(w, r, v)
		} else {
			w.WriteHeader(http.StatusNotFound)
			return
		}
	}
}

func (s songs) send(w http.ResponseWriter, r *http.Request, i int) {
	enc := json.NewEncoder(w)
	enc.SetIndent("", " ")
	enc.Encode(s[:i])
}

// Serves playlists
func getAPI(songs songs) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		var err error
		switch r.URL.Query().Get("sort") {
		case "bytitle":
			if v, ok := sortcache["bytitle"]; !ok {
				apiMutex.Lock()
				sort.Sort(byTitle(songs))
				songs.send(w, r, len(songs))
				sortcache["bytitle"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by title failed", "error", err)
					delete(sortcache, "bytitle")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byartist":
			if v, ok := sortcache["byartist"]; !ok {
				apiMutex.Lock()
				sort.Sort(byArtist(songs))
				songs.send(w, r, len(songs))
				sortcache["byartist"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by artist failed", "error", err)
					delete(sortcache, "byartist")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byalbum":
			if v, ok := sortcache["byalbum"]; !ok {
				apiMutex.Lock()
				sort.Sort(byAlbum(songs))
				songs.send(w, r, len(songs))
				sortcache["byalbum"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by album failed", "error", err)
					delete(sortcache, "byalbum")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byshuffle":
			apiMutex.Lock()
			rand.Seed(time.Now().UnixNano())
			for i := len(songs) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			songs.send(w, r, len(songs))
			apiMutex.Unlock()
		case "byleast":
			apiMutex.Lock()
			sort.Sort(byLeast(songs))
			songs.send(w, r, len(songs))
			apiMutex.Unlock()
		case "bymost":
			apiMutex.Lock()
			sort.Sort(sort.Reverse(byLeast(songs)))
			songs.send(w, r, len(songs))
			apiMutex.Unlock()
		case "byfavorite":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			songs.send(w, r, likedCount)
			apiMutex.Unlock()
		case "byfavshuffle":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			rand.Seed(time.Now().UnixNano())
			for i := len(songs[0:likedCount]) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			songs.send(w, r, likedCount)
			apiMutex.Unlock()
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

// Serves song/album artwork
func artwork(w http.ResponseWriter, r *http.Request) {
	if err := authorize(w, r); err != nil {
		srvlog.Warn("Acess denied", "err", err)
		return
	}
	id := r.URL.Query().Get("id")
	reg := regexp.MustCompile("^\\w+$")
	if !reg.MatchString(id) {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file := fmt.Sprintf("./artcache/%s", id)
	_, err := os.Stat(file)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	http.ServeFile(w, r, file)
}

// Save playcount to a file
func savePlayCount(playcount map[string]int64) {
	playcountFile.Truncate(0)
	enc := json.NewEncoder(playcountFile)
	enc.SetIndent("", " ")
	err := enc.Encode(playcount)
	if err != nil {
		srvlog.Crit("could not encode playcount json", "error", err)
	}
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

// PagesProject represents a Pages project.
type PagesProject struct {
	Name                string                        `json:"name"`
	ID                  string                        `json:"id"`
	CreatedOn           *time.Time                    `json:"created_on"`
	SubDomain           string                        `json:"subdomain"`
	Domains             []string                      `json:"domains,omitempty"`
	Source              PagesProjectSource            `json:"source"`
	BuildConfig         PagesProjectBuildConfig       `json:"build_config"`
	DeploymentConfigs   PagesProjectDeploymentConfigs `json:"deployment_configs"`
	LatestDeployment    PagesProjectDeployment        `json:"latest_deployment"`
	CanonicalDeployment PagesProjectDeployment        `json:"canonical_deployment"`
}

// PagesProjectSource represents the configuration of a Pages project source.
type PagesProjectSource struct {
	Type   string                    `json:"type"`
	Config *PagesProjectSourceConfig `json:"config"`
}

// PagesProjectSourceConfig represents the properties use to configure a Pages project source.
type PagesProjectSourceConfig struct {
	Owner              string `json:"owner"`
	RepoName           string `json:"repo_name"`
	ProductionBranch   string `json:"production_branch"`
	PRCommentsEnabled  bool   `json:"pr_comments_enabled"`
	DeploymentsEnabled bool   `json:"deployments_enabled"`
}

// PagesProjectBuildConfig represents the configuration of a Pages project build process.
type PagesProjectBuildConfig struct {
	BuildCommand      string `json:"build_command"`
	DestinationDir    string `json:"destination_dir"`
	RootDir           string `json:"root_dir"`
	WebAnalyticsTag   string `json:"web_analytics_tag"`
	WebAnalyticsToken string `json:"web_analytics_token"`
}

// PagesProjectDeploymentConfigs represents the configuration for deployments in a Pages project.
type PagesProjectDeploymentConfigs struct {
	Preview    PagesProjectDeploymentConfigEnvironment `json:"preview"`
	Production PagesProjectDeploymentConfigEnvironment `json:"production"`
}

// PagesProjectDeploymentConfigEnvironment represents the configuration for preview or production deploys.
type PagesProjectDeploymentConfigEnvironment struct {
	EnvVars map[string]PagesProjectDeploymentVar `json:"env_vars"`
}

// PagesProjectDeploymentConfigBuildVersion represents a value for a BUILD_VERSION.
type PagesProjectDeploymentVar struct {
	Value string `json:"value"`
}

// PagesProjectDeployment represents a deployment to a Pages project.
type PagesProjectDeployment struct {
	ID                string                        `json:"id"`
	ShortID           string                        `json:"short_id"`
	ProjectID         string                        `json:"project_id"`
	ProjectName       string                        `json:"project_name"`
	Environment       string                        `json:"environment"`
	URL               string                        `json:"url"`
	CreatedOn         *time.Time                    `json:"created_on"`
	ModifiedOn        *time.Time                    `json:"modified_on"`
	Aliases           []string                      `json:"aliases,omitempty"`
	LatestStage       PagesProjectDeploymentStage   `json:"latest_stage"`
	EnvVars           map[string]map[string]string  `json:"env_vars"`
	DeploymentTrigger PagesProjectDeploymentTrigger `json:"deployment_trigger"`
	Stages            []PagesProjectDeploymentStage `json:"stages"`
	BuildConfig       PagesProjectBuildConfig       `json:"build_config"`
	Source            PagesProjectSource            `json:"source"`
}

// PagesProjectDeploymentStage represents an individual stage in a Pages project deployment.
type PagesProjectDeploymentStage struct {
	Name      string     `json:"name"`
	StartedOn *time.Time `json:"started_on,omitempty"`
	EndedOn   *time.Time `json:"ended_on,omitempty"`
	Status    string     `json:"status"`
}

// PagesProjectDeploymentTrigger represents information about what caused a deployment.
type PagesProjectDeploymentTrigger struct {
	Type     string                                 `json:"type"`
	Metadata *PagesProjectDeploymentTriggerMetadata `json:"metadata"`
}

// PagesProjectDeploymentTriggerMetadata represents additional information about the cause of a deployment.
type PagesProjectDeploymentTriggerMetadata struct {
	Branch        string `json:"branch"`
	CommitHash    string `json:"commit_hash"`
	CommitMessage string `json:"commit_message"`
}

type pagesProjectResponse struct {
	Response
	Result PagesProject `json:"result"`
}

type pagesProjectListResponse struct {
	Response
	Result     []PagesProject `json:"result"`
	ResultInfo `json:"result_info"`
}

// ListPagesProjects returns all Pages projects for an account.
//
// API reference: https://api.cloudflare.com/#pages-project-get-projects
func (api *API) ListPagesProjects(ctx context.Context, accountID string, pageOpts PaginationOptions) ([]PagesProject, ResultInfo, error) {
	v := url.Values{}
	if pageOpts.PerPage > 0 {
		v.Set("per_page", strconv.Itoa(pageOpts.PerPage))
	}
	if pageOpts.Page > 0 {
		v.Set("page", strconv.Itoa(pageOpts.Page))
	}

	uri := fmt.Sprintf("/accounts/%s/pages/projects", accountID)
	if len(v) > 0 {
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []PagesProject{}, ResultInfo{}, err
	}
	var r pagesProjectListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []PagesProject{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, r.ResultInfo, nil
}

// PagesProject returns a single Pages project by name.
//
// API reference: https://api.cloudflare.com/#pages-project-get-project
func (api *API) PagesProject(ctx context.Context, accountID, projectName string) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// CreatePagesProject creates a new Pages project in an account.
//
// API reference: https://api.cloudflare.com/#pages-project-create-project
func (api *API) CreatePagesProject(ctx context.Context, accountID string, pagesProject PagesProject) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects", accountID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, pagesProject)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// UpdatePagesProject updates an existing Pages project.
//
// API reference: https://api.cloudflare.com/#pages-project-update-project
func (api *API) UpdatePagesProject(ctx context.Context, accountID, projectName string, pagesProject PagesProject) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, pagesProject)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// DeletePagesProject deletes a Pages project by name.
//
// API reference: https://api.cloudflare.com/#pages-project-delete-project
func (api *API) DeletePagesProject(ctx context.Context, accountID, projectName string) error {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}
<commit_msg>pages_project: make `name` omitempty<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

// PagesProject represents a Pages project.
type PagesProject struct {
	Name                string                        `json:"name,omitempty"`
	ID                  string                        `json:"id"`
	CreatedOn           *time.Time                    `json:"created_on"`
	SubDomain           string                        `json:"subdomain"`
	Domains             []string                      `json:"domains,omitempty"`
	Source              PagesProjectSource            `json:"source"`
	BuildConfig         PagesProjectBuildConfig       `json:"build_config"`
	DeploymentConfigs   PagesProjectDeploymentConfigs `json:"deployment_configs"`
	LatestDeployment    PagesProjectDeployment        `json:"latest_deployment"`
	CanonicalDeployment PagesProjectDeployment        `json:"canonical_deployment"`
}

// PagesProjectSource represents the configuration of a Pages project source.
type PagesProjectSource struct {
	Type   string                    `json:"type"`
	Config *PagesProjectSourceConfig `json:"config"`
}

// PagesProjectSourceConfig represents the properties use to configure a Pages project source.
type PagesProjectSourceConfig struct {
	Owner              string `json:"owner"`
	RepoName           string `json:"repo_name"`
	ProductionBranch   string `json:"production_branch"`
	PRCommentsEnabled  bool   `json:"pr_comments_enabled"`
	DeploymentsEnabled bool   `json:"deployments_enabled"`
}

// PagesProjectBuildConfig represents the configuration of a Pages project build process.
type PagesProjectBuildConfig struct {
	BuildCommand      string `json:"build_command"`
	DestinationDir    string `json:"destination_dir"`
	RootDir           string `json:"root_dir"`
	WebAnalyticsTag   string `json:"web_analytics_tag"`
	WebAnalyticsToken string `json:"web_analytics_token"`
}

// PagesProjectDeploymentConfigs represents the configuration for deployments in a Pages project.
type PagesProjectDeploymentConfigs struct {
	Preview    PagesProjectDeploymentConfigEnvironment `json:"preview"`
	Production PagesProjectDeploymentConfigEnvironment `json:"production"`
}

// PagesProjectDeploymentConfigEnvironment represents the configuration for preview or production deploys.
type PagesProjectDeploymentConfigEnvironment struct {
	EnvVars map[string]PagesProjectDeploymentVar `json:"env_vars"`
}

// PagesProjectDeploymentConfigBuildVersion represents a value for a BUILD_VERSION.
type PagesProjectDeploymentVar struct {
	Value string `json:"value"`
}

// PagesProjectDeployment represents a deployment to a Pages project.
type PagesProjectDeployment struct {
	ID                string                        `json:"id"`
	ShortID           string                        `json:"short_id"`
	ProjectID         string                        `json:"project_id"`
	ProjectName       string                        `json:"project_name"`
	Environment       string                        `json:"environment"`
	URL               string                        `json:"url"`
	CreatedOn         *time.Time                    `json:"created_on"`
	ModifiedOn        *time.Time                    `json:"modified_on"`
	Aliases           []string                      `json:"aliases,omitempty"`
	LatestStage       PagesProjectDeploymentStage   `json:"latest_stage"`
	EnvVars           map[string]map[string]string  `json:"env_vars"`
	DeploymentTrigger PagesProjectDeploymentTrigger `json:"deployment_trigger"`
	Stages            []PagesProjectDeploymentStage `json:"stages"`
	BuildConfig       PagesProjectBuildConfig       `json:"build_config"`
	Source            PagesProjectSource            `json:"source"`
}

// PagesProjectDeploymentStage represents an individual stage in a Pages project deployment.
type PagesProjectDeploymentStage struct {
	Name      string     `json:"name"`
	StartedOn *time.Time `json:"started_on,omitempty"`
	EndedOn   *time.Time `json:"ended_on,omitempty"`
	Status    string     `json:"status"`
}

// PagesProjectDeploymentTrigger represents information about what caused a deployment.
type PagesProjectDeploymentTrigger struct {
	Type     string                                 `json:"type"`
	Metadata *PagesProjectDeploymentTriggerMetadata `json:"metadata"`
}

// PagesProjectDeploymentTriggerMetadata represents additional information about the cause of a deployment.
type PagesProjectDeploymentTriggerMetadata struct {
	Branch        string `json:"branch"`
	CommitHash    string `json:"commit_hash"`
	CommitMessage string `json:"commit_message"`
}

type pagesProjectResponse struct {
	Response
	Result PagesProject `json:"result"`
}

type pagesProjectListResponse struct {
	Response
	Result     []PagesProject `json:"result"`
	ResultInfo `json:"result_info"`
}

// ListPagesProjects returns all Pages projects for an account.
//
// API reference: https://api.cloudflare.com/#pages-project-get-projects
func (api *API) ListPagesProjects(ctx context.Context, accountID string, pageOpts PaginationOptions) ([]PagesProject, ResultInfo, error) {
	v := url.Values{}
	if pageOpts.PerPage > 0 {
		v.Set("per_page", strconv.Itoa(pageOpts.PerPage))
	}
	if pageOpts.Page > 0 {
		v.Set("page", strconv.Itoa(pageOpts.Page))
	}

	uri := fmt.Sprintf("/accounts/%s/pages/projects", accountID)
	if len(v) > 0 {
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []PagesProject{}, ResultInfo{}, err
	}
	var r pagesProjectListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []PagesProject{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, r.ResultInfo, nil
}

// PagesProject returns a single Pages project by name.
//
// API reference: https://api.cloudflare.com/#pages-project-get-project
func (api *API) PagesProject(ctx context.Context, accountID, projectName string) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// CreatePagesProject creates a new Pages project in an account.
//
// API reference: https://api.cloudflare.com/#pages-project-create-project
func (api *API) CreatePagesProject(ctx context.Context, accountID string, pagesProject PagesProject) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects", accountID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, pagesProject)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// UpdatePagesProject updates an existing Pages project.
//
// API reference: https://api.cloudflare.com/#pages-project-update-project
func (api *API) UpdatePagesProject(ctx context.Context, accountID, projectName string, pagesProject PagesProject) (PagesProject, error) {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, pagesProject)
	if err != nil {
		return PagesProject{}, err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return PagesProject{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// DeletePagesProject deletes a Pages project by name.
//
// API reference: https://api.cloudflare.com/#pages-project-delete-project
func (api *API) DeletePagesProject(ctx context.Context, accountID, projectName string) error {
	uri := fmt.Sprintf("/accounts/%s/pages/projects/%s", accountID, projectName)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}
	var r pagesProjectResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}
<|endoftext|>


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

//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go

package x509

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
	"sync"
)

var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

// This code is only used when compiling without cgo.
// It is here, instead of root_nocgo_darwin.go, so that tests can check it
// even if the tests are run with cgo enabled.
// The linker will not include these unused functions in binaries built with cgo enabled.

// execSecurityRoots finds the macOS list of trusted root certificates
// using only command-line tools. This is our fallback path when cgo isn't available.
//
// The strategy is as follows:
//
// 1. Run "security trust-settings-export" and "security
//    trust-settings-export -d" to discover the set of certs with some
//    user-tweaked trust policy. We're too lazy to parse the XML (at
//    least at this stage of Go 1.8) to understand what the trust
//    policy actually is. We just learn that there is _some_ policy.
//
// 2. Run "security find-certificate" to dump the list of system root
//    CAs in PEM format.
//
// 3. For each dumped cert, conditionally verify it with "security
//    verify-cert" if that cert was in the set discovered in Step 1.
//    Without the Step 1 optimization, running "security verify-cert"
//    150-200 times takes 3.5 seconds. With the optimization, the
//    whole process takes about 180 milliseconds with 1 untrusted root
//    CA. (Compared to 110ms in the cgo path)
func execSecurityRoots() (*CertPool, error) {
	hasPolicy, err := getCertsWithTrustPolicy()
	if err != nil {
		return nil, err
	}
	if debugExecDarwinRoots {
		println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy)))
	}

	args := []string{"find-certificate", "-a", "-p",
		"/System/Library/Keychains/SystemRootCertificates.keychain",
		"/Library/Keychains/System.keychain",
	}

	u, err := user.Current()
	if err != nil {
		if debugExecDarwinRoots {
			println(fmt.Sprintf("crypto/x509: get current user: %v", err))
		}
	} else {
		args = append(args,
			filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"),

			// Fresh installs of Sierra use a slightly different path for the login keychain
			filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"),
		)
	}

	cmd := exec.Command("/usr/bin/security", args...)
	data, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var (
		mu          sync.Mutex
		roots       = NewCertPool()
		numVerified int // number of execs of 'security verify-cert', for debug stats
	)

	blockCh := make(chan *pem.Block)
	var wg sync.WaitGroup

	// Using 4 goroutines to pipe into verify-cert seems to be
	// about the best we can do. The verify-cert binary seems to
	// just RPC to another server with coarse locking anyway, so
	// running 16 at a time for instance doesn't help at all. Due
	// to the "if hasPolicy" check below, though, we will rarely
	// (or never) call verify-cert on stock macOS systems, though.
	// The hope is that we only call verify-cert when the user has
	// tweaked their trust policy. These 4 goroutines are only
	// defensive in the pathological case of many trust edits.
	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for block := range blockCh {
				cert, err := ParseCertificate(block.Bytes)
				if err != nil {
					continue
				}
				sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes))

				valid := true
				verifyChecks := 0
				if hasPolicy[sha1CapHex] {
					verifyChecks++
					if !verifyCertWithSystem(block, cert) {
						valid = false
					}
				}

				mu.Lock()
				numVerified += verifyChecks
				if valid {
					roots.AddCert(cert)
				}
				mu.Unlock()
			}
		}()
	}
	for len(data) > 0 {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}
		blockCh <- block
	}
	close(blockCh)
	wg.Wait()

	if debugExecDarwinRoots {
		mu.Lock()
		defer mu.Unlock()
		println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified))
	}

	return roots, nil
}

func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
	data := pem.EncodeToMemory(block)

	f, err := ioutil.TempFile("", "cert")
	if err != nil {
		fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
		return false
	}
	defer os.Remove(f.Name())
	if _, err := f.Write(data); err != nil {
		fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
		return false
	}
	if err := f.Close(); err != nil {
		fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
		return false
	}
	cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L")
	var stderr bytes.Buffer
	if debugExecDarwinRoots {
		cmd.Stderr = &stderr
	}
	if err := cmd.Run(); err != nil {
		if debugExecDarwinRoots {
			println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject.CommonName, bytes.TrimSpace(stderr.Bytes())))
		}
		return false
	}
	if debugExecDarwinRoots {
		println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject.CommonName))
	}
	return true
}

// getCertsWithTrustPolicy returns the set of certs that have a
// possibly-altered trust policy. The keys of the map are capitalized
// sha1 hex of the raw cert.
// They are the certs that should be checked against `security
// verify-cert` to see whether the user altered the default trust
// settings. This code is only used for cgo-disabled builds.
func getCertsWithTrustPolicy() (map[string]bool, error) {
	set := map[string]bool{}
	td, err := ioutil.TempDir("", "x509trustpolicy")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(td)
	run := func(file string, args ...string) error {
		file = filepath.Join(td, file)
		args = append(args, file)
		cmd := exec.Command("/usr/bin/security", args...)
		var stderr bytes.Buffer
		cmd.Stderr = &stderr
		if err := cmd.Run(); err != nil {
			// If there are no trust settings, the
			// `security trust-settings-export` command
			// fails with:
			//    exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found.
			// Rather than match on English substrings that are probably
			// localized on macOS, just interpret any failure to mean that
			// there are no trust settings.
			if debugExecDarwinRoots {
				println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes()))
			}
			return nil
		}

		f, err := os.Open(file)
		if err != nil {
			return err
		}
		defer f.Close()

		// Gather all the runs of 40 capitalized hex characters.
		br := bufio.NewReader(f)
		var hexBuf bytes.Buffer
		for {
			b, err := br.ReadByte()
			isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9')
			if isHex {
				hexBuf.WriteByte(b)
			} else {
				if hexBuf.Len() == 40 {
					set[hexBuf.String()] = true
				}
				hexBuf.Reset()
			}
			if err == io.EOF {
				break
			}
			if err != nil {
				return err
			}
		}

		return nil
	}
	if err := run("user", "trust-settings-export"); err != nil {
		return nil, fmt.Errorf("dump-trust-settings (user): %v", err)
	}
	if err := run("admin", "trust-settings-export", "-d"); err != nil {
		return nil, fmt.Errorf("dump-trust-settings (admin): %v", err)
	}
	return set, nil
}
<commit_msg>crypto/x509: better debug output for verify-cert calls<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go

package x509

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
	"sync"
)

var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

// This code is only used when compiling without cgo.
// It is here, instead of root_nocgo_darwin.go, so that tests can check it
// even if the tests are run with cgo enabled.
// The linker will not include these unused functions in binaries built with cgo enabled.

// execSecurityRoots finds the macOS list of trusted root certificates
// using only command-line tools. This is our fallback path when cgo isn't available.
//
// The strategy is as follows:
//
// 1. Run "security trust-settings-export" and "security
//    trust-settings-export -d" to discover the set of certs with some
//    user-tweaked trust policy. We're too lazy to parse the XML (at
//    least at this stage of Go 1.8) to understand what the trust
//    policy actually is. We just learn that there is _some_ policy.
//
// 2. Run "security find-certificate" to dump the list of system root
//    CAs in PEM format.
//
// 3. For each dumped cert, conditionally verify it with "security
//    verify-cert" if that cert was in the set discovered in Step 1.
//    Without the Step 1 optimization, running "security verify-cert"
//    150-200 times takes 3.5 seconds. With the optimization, the
//    whole process takes about 180 milliseconds with 1 untrusted root
//    CA. (Compared to 110ms in the cgo path)
func execSecurityRoots() (*CertPool, error) {
	hasPolicy, err := getCertsWithTrustPolicy()
	if err != nil {
		return nil, err
	}
	if debugExecDarwinRoots {
		println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy)))
	}

	args := []string{"find-certificate", "-a", "-p",
		"/System/Library/Keychains/SystemRootCertificates.keychain",
		"/Library/Keychains/System.keychain",
	}

	u, err := user.Current()
	if err != nil {
		if debugExecDarwinRoots {
			println(fmt.Sprintf("crypto/x509: get current user: %v", err))
		}
	} else {
		args = append(args,
			filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"),

			// Fresh installs of Sierra use a slightly different path for the login keychain
			filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"),
		)
	}

	cmd := exec.Command("/usr/bin/security", args...)
	data, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var (
		mu          sync.Mutex
		roots       = NewCertPool()
		numVerified int // number of execs of 'security verify-cert', for debug stats
	)

	blockCh := make(chan *pem.Block)
	var wg sync.WaitGroup

	// Using 4 goroutines to pipe into verify-cert seems to be
	// about the best we can do. The verify-cert binary seems to
	// just RPC to another server with coarse locking anyway, so
	// running 16 at a time for instance doesn't help at all. Due
	// to the "if hasPolicy" check below, though, we will rarely
	// (or never) call verify-cert on stock macOS systems, though.
	// The hope is that we only call verify-cert when the user has
	// tweaked their trust policy. These 4 goroutines are only
	// defensive in the pathological case of many trust edits.
	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for block := range blockCh {
				cert, err := ParseCertificate(block.Bytes)
				if err != nil {
					continue
				}
				sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes))

				valid := true
				verifyChecks := 0
				if hasPolicy[sha1CapHex] {
					verifyChecks++
					if !verifyCertWithSystem(block, cert) {
						valid = false
					}
				}

				mu.Lock()
				numVerified += verifyChecks
				if valid {
					roots.AddCert(cert)
				}
				mu.Unlock()
			}
		}()
	}
	for len(data) > 0 {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}
		blockCh <- block
	}
	close(blockCh)
	wg.Wait()

	if debugExecDarwinRoots {
		mu.Lock()
		defer mu.Unlock()
		println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified))
	}

	return roots, nil
}

func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
	data := pem.EncodeToMemory(block)

	f, err := ioutil.TempFile("", "cert")
	if err != nil {
		fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
		return false
	}
	defer os.Remove(f.Name())
	if _, err := f.Write(data); err != nil {
		fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
		return false
	}
	if err := f.Close(); err != nil {
		fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
		return false
	}
	cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L")
	var stderr bytes.Buffer
	if debugExecDarwinRoots {
		cmd.Stderr = &stderr
	}
	if err := cmd.Run(); err != nil {
		if debugExecDarwinRoots {
			println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject, bytes.TrimSpace(stderr.Bytes())))
		}
		return false
	}
	if debugExecDarwinRoots {
		println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject))
	}
	return true
}

// getCertsWithTrustPolicy returns the set of certs that have a
// possibly-altered trust policy. The keys of the map are capitalized
// sha1 hex of the raw cert.
// They are the certs that should be checked against `security
// verify-cert` to see whether the user altered the default trust
// settings. This code is only used for cgo-disabled builds.
func getCertsWithTrustPolicy() (map[string]bool, error) {
	set := map[string]bool{}
	td, err := ioutil.TempDir("", "x509trustpolicy")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(td)
	run := func(file string, args ...string) error {
		file = filepath.Join(td, file)
		args = append(args, file)
		cmd := exec.Command("/usr/bin/security", args...)
		var stderr bytes.Buffer
		cmd.Stderr = &stderr
		if err := cmd.Run(); err != nil {
			// If there are no trust settings, the
			// `security trust-settings-export` command
			// fails with:
			//    exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found.
			// Rather than match on English substrings that are probably
			// localized on macOS, just interpret any failure to mean that
			// there are no trust settings.
			if debugExecDarwinRoots {
				println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes()))
			}
			return nil
		}

		f, err := os.Open(file)
		if err != nil {
			return err
		}
		defer f.Close()

		// Gather all the runs of 40 capitalized hex characters.
		br := bufio.NewReader(f)
		var hexBuf bytes.Buffer
		for {
			b, err := br.ReadByte()
			isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9')
			if isHex {
				hexBuf.WriteByte(b)
			} else {
				if hexBuf.Len() == 40 {
					set[hexBuf.String()] = true
				}
				hexBuf.Reset()
			}
			if err == io.EOF {
				break
			}
			if err != nil {
				return err
			}
		}

		return nil
	}
	if err := run("user", "trust-settings-export"); err != nil {
		return nil, fmt.Errorf("dump-trust-settings (user): %v", err)
	}
	if err := run("admin", "trust-settings-export", "-d"); err != nil {
		return nil, fmt.Errorf("dump-trust-settings (admin): %v", err)
	}
	return set, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	pb "client/pb"
	"google.golang.org/grpc"
)

func main() {
	// serverAddr := "127.0.0.1:8080"
	host := "server"
	port := "8080"

	serverAddr := fmt.Sprintf("%s:%s", host, port)

	conn, err := grpc.Dial(serverAddr, grpc.WithInsecure())
	if err != nil {
		fmt.Println(err)
	}

	defer conn.Close()

	client := pb.NewPingpongClient(conn)
	feature, err := client.Ping(context.Background(), &pb.PingRequest{
		Ping: "ping",
		Pong: "",
	})
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(feature)
}
<commit_msg>[Modify] Set to call endpoint every 3 seconds @client/client.go<commit_after>package main

import (
	"context"
	"fmt"

	pb "client/pb"
	"google.golang.org/grpc"
	"time"
)

func main() {
	host := "server"
	port := "8080"

	serverAddr := fmt.Sprintf("%s:%s", host, port)

	for {
		conn, err := grpc.Dial(serverAddr, grpc.WithInsecure())
		if err != nil {
			fmt.Println(err)
		}

		defer conn.Close()

		client := pb.NewPingpongClient(conn)
		feature, err := client.Ping(context.Background(), &pb.PingRequest{
			Ping: "ping",
			Pong: "",
		})
		if err != nil {
			fmt.Println(err)
		}

		fmt.Println(feature)

		time.Sleep(3 * time.Second)
	}
}
<|endoftext|>


<commit_before>package gcp

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"

	"github.com/EngineerBetter/concourse-up/resource"
	"github.com/EngineerBetter/concourse-up/util"
	"github.com/EngineerBetter/concourse-up/util/yaml"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)

// Environment holds all the parameters GCP IAAS needs
type Environment struct {
	AccessKeyID           string
	ATCSecurityGroup      string
	BlobstoreBucket       string
	DBCACert              string
	DBHost                string
	DBName                string
	DBPassword            string
	DBPort                string
	DBUsername            string
	DefaultKeyName        string
	DefaultSecurityGroups []string
	ExternalIP            string
	GCPCredentialsJSON    string
	InternalCIDR          string
	InternalGateway       string
	InternalIP            string
	Network               string
	Preemptible           bool
	PrivateKey            string
	PrivateSubnetID       string
	ProjectID             string
	PublicSubnetID        string
	Region                string
	S3AWSAccessKeyID      string
	S3AWSSecretAccessKey  string
	SecretAccessKey       string
	Tags                  []string
	VMSecurityGroup       string
	Zone                  string
	Credentials           string
}

var allOperations = resource.AWSCPIOps + resource.ExternalIPOps + resource.DirectorCustomOps

// ConfigureDirectorManifestCPI interpolates all the Environment parameters and
// required release versions into ready to use Director manifest
func (e Environment) ConfigureDirectorManifestCPI(manifest string) (string, error) {
	cpiResource := resource.Get(resource.AWSCPI)
	stemcellResource := resource.Get(resource.AWSStemcell)
	return yaml.Interpolate(manifest, allOperations, map[string]interface{}{
		"zone":       e.Zone,
		"network":    e.Network,
		"subnetwork": e.PublicSubnetID,
		"tags":       e.Tags,

		"cpi_url":                  cpiResource.URL,
		"cpi_version":              cpiResource.Version,
		"cpi_sha1":                 cpiResource.SHA1,
		"stemcell_url":             stemcellResource.URL,
		"stemcell_sha1":            stemcellResource.SHA1,
		"internal_cidr":            e.InternalCIDR,
		"internal_gw":              e.InternalGateway,
		"internal_ip":              e.InternalIP,
		"access_key_id":            e.AccessKeyID,
		"secret_access_key":        e.SecretAccessKey,
		"region":                   e.Region,
		"az":                       e.Zone,
		"default_key_name":         e.DefaultKeyName,
		"default_security_groups":  e.DefaultSecurityGroups,
		"private_key":              e.PrivateKey,
		"external_ip":              e.ExternalIP,
		"blobstore_bucket":         e.BlobstoreBucket,
		"db_ca_cert":               e.DBCACert,
		"db_host":                  e.DBHost,
		"db_name":                  e.DBName,
		"db_password":              e.DBPassword,
		"db_port":                  e.DBPort,
		"db_username":              e.DBUsername,
		"s3_aws_access_key_id":     e.S3AWSAccessKeyID,
		"s3_aws_secret_access_key": e.S3AWSSecretAccessKey,
	})
}

type awsCloudConfigParams struct {
	ATCSecurityGroupID string
	AvailabilityZone   string
	PrivateSubnetID    string
	PublicSubnetID     string
	Preemptible        bool
	VMsSecurityGroupID string
}

// ConfigureDirectorCloudConfig inserts values from the environment into the config template passed as argument
func (e Environment) ConfigureDirectorCloudConfig(cloudConfig string) (string, error) {

	templateParams := awsCloudConfigParams{
		AvailabilityZone:   e.Zone,
		VMsSecurityGroupID: e.VMSecurityGroup,
		ATCSecurityGroupID: e.ATCSecurityGroup,
		PublicSubnetID:     e.PublicSubnetID,
		PrivateSubnetID:    e.PrivateSubnetID,
		Preemptible:        e.Preemptible,
	}

	cc, err := util.RenderTemplate(cloudConfig, templateParams)
	if cc == nil {
		return "", err
	}
	return string(cc), err
}

// ConfigureConcourseStemcell returns the stemcell location string for an AWS specific stemcell for the required concourse version
func (e Environment) ConfigureConcourseStemcell(versions string) (string, error) {
	var ops []struct {
		Path  string
		Value json.RawMessage
	}
	err := json.Unmarshal([]byte(versions), &ops)
	if err != nil {
		return "", err
	}
	var version string
	for _, op := range ops {
		if op.Path != "/stemcells/alias=xenial/version" {
			continue
		}
		err := json.Unmarshal(op.Value, &version)
		if err != nil {
			return "", err
		}
	}
	if version == "" {
		return "", errors.New("did not find stemcell version in versions.json")
	}
	return fmt.Sprintf("https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-%s-aws-xen-hvm-ubuntu-xenial-go_agent.tgz", version), nil
}

// Store holds the abstraction of a aws storage artifact
type Store struct {
	s3     s3iface.S3API
	bucket string
}

// NewStore returns a reference to a new Store
func NewStore(s3 s3iface.S3API, bucket string) *Store {
	return &Store{
		s3:     s3,
		bucket: bucket,
	}
}

// Get returns the contents of a Store element identified with a key
func (s *Store) Get(key string) ([]byte, error) {
	result, err := s.s3.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	})
	if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == s3.ErrCodeNoSuchKey {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	defer result.Body.Close()
	return ioutil.ReadAll(result.Body)
}

// Set stores the contents of a Store element identified with a key
func (s *Store) Set(key string, value []byte) error {
	_, err := s.s3.PutObject(&s3.PutObjectInput{
		Body:   bytes.NewReader(value),
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	})
	return err
}
<commit_msg>update ConfigureDirectorManifestCPI and ConfigureDirectorCloudConfig within gcp.go these now use GCP-specific values<commit_after>package gcp

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"

	"github.com/EngineerBetter/concourse-up/resource"
	"github.com/EngineerBetter/concourse-up/util"
	"github.com/EngineerBetter/concourse-up/util/yaml"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)

// Environment holds all the parameters GCP IAAS needs
type Environment struct {
	AccessKeyID           string
	ATCSecurityGroup      string
	BlobstoreBucket       string
	DBCACert              string
	DBHost                string
	DBName                string
	DBPassword            string
	DBPort                string
	DBUsername            string
	DefaultKeyName        string
	DefaultSecurityGroups []string
	ExternalIP            string
	GCPCredentialsJSON    string
	InternalCIDR          string
	InternalGateway       string
	InternalIP            string
	Network               string
	Preemptible           bool
	PrivateKey            string
	PrivateSubnetID       string
	ProjectID             string
	PublicSubnetID        string
	Region                string
	S3AWSAccessKeyID      string
	S3AWSSecretAccessKey  string
	SecretAccessKey       string
	Tags                  []string
	VMSecurityGroup       string
	Zone                  string
	Credentials           string
}

var allOperations = resource.AWSCPIOps + resource.ExternalIPOps

// ConfigureDirectorManifestCPI interpolates all the Environment parameters and
// required release versions into ready to use Director manifest
func (e Environment) ConfigureDirectorManifestCPI(manifest string) (string, error) {
	return yaml.Interpolate(manifest, allOperations, map[string]interface{}{
		"external_ip":          e.ExternalIP,
		"gcp_credentials_json": e.DefaultKeyName,
		"network":              e.Network,
		"project_id":           e.ProjectID,
		"subnetwork":           e.PublicSubnetID,
		"tags":                 e.Tags,
		"zone":                 e.Zone,
	})
}

type gcpCloudConfigParams struct {
	ATCSecurityGroupID string
	Zone               string
	PrivateSubnetID    string
	PublicSubnetID     string
	Preemptible        bool
	VMsSecurityGroupID string
}

// ConfigureDirectorCloudConfig inserts values from the environment into the config template passed as argument
func (e Environment) ConfigureDirectorCloudConfig(cloudConfig string) (string, error) {

	templateParams := gcpCloudConfigParams{
		Zone:            e.Zone,
		PublicSubnetID:  e.PublicSubnetID,
		PrivateSubnetID: e.PrivateSubnetID,
		Preemptible:     e.Preemptible,
	}

	cc, err := util.RenderTemplate(cloudConfig, templateParams)
	if cc == nil {
		return "", err
	}
	return string(cc), err
}

// ConfigureConcourseStemcell returns the stemcell location string for an AWS specific stemcell for the required concourse version
func (e Environment) ConfigureConcourseStemcell(versions string) (string, error) {
	var ops []struct {
		Path  string
		Value json.RawMessage
	}
	err := json.Unmarshal([]byte(versions), &ops)
	if err != nil {
		return "", err
	}
	var version string
	for _, op := range ops {
		if op.Path != "/stemcells/alias=xenial/version" {
			continue
		}
		err := json.Unmarshal(op.Value, &version)
		if err != nil {
			return "", err
		}
	}
	if version == "" {
		return "", errors.New("did not find stemcell version in versions.json")
	}
	return fmt.Sprintf("https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-%s-aws-xen-hvm-ubuntu-xenial-go_agent.tgz", version), nil
}

// Store holds the abstraction of a aws storage artifact
type Store struct {
	s3     s3iface.S3API
	bucket string
}

// NewStore returns a reference to a new Store
func NewStore(s3 s3iface.S3API, bucket string) *Store {
	return &Store{
		s3:     s3,
		bucket: bucket,
	}
}

// Get returns the contents of a Store element identified with a key
func (s *Store) Get(key string) ([]byte, error) {
	result, err := s.s3.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	})
	if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == s3.ErrCodeNoSuchKey {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	defer result.Body.Close()
	return ioutil.ReadAll(result.Body)
}

// Set stores the contents of a Store element identified with a key
func (s *Store) Set(key string, value []byte) error {
	_, err := s.s3.PutObject(&s3.PutObjectInput{
		Body:   bytes.NewReader(value),
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	})
	return err
}
<|endoftext|>