<commit_before>// Copyright 2016 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.

// +build go1.7,amd64,!gccgo,!appengine

package chacha20poly1305

import (
	"encoding/binary"

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

//go:noescape
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool

//go:noescape
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)

var (
	useASM  = cpu.X86.HasSSSE3
	useAVX2 = cpu.X86.HasAVX2
)

// setupState writes a ChaCha20 input matrix to state. See
// https://tools.ietf.org/html/rfc7539#section-2.3.
func setupState(state *[16]uint32, key *[8]uint32, nonce []byte) {
	state[0] = 0x61707865
	state[1] = 0x3320646e
	state[2] = 0x79622d32
	state[3] = 0x6b206574

	state[4] = key[0]
	state[5] = key[1]
	state[6] = key[2]
	state[7] = key[3]
	state[8] = key[4]
	state[9] = key[5]
	state[10] = key[6]
	state[11] = key[7]

	state[12] = 0
	state[13] = binary.LittleEndian.Uint32(nonce[:4])
	state[14] = binary.LittleEndian.Uint32(nonce[4:8])
	state[15] = binary.LittleEndian.Uint32(nonce[8:12])
}

func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
	if !useASM {
		return c.sealGeneric(dst, nonce, plaintext, additionalData)
	}

	var state [16]uint32
	setupState(&state, &c.key, nonce)

	ret, out := sliceForAppend(dst, len(plaintext)+16)
	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
	return ret
}

func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
	if !useASM {
		return c.openGeneric(dst, nonce, ciphertext, additionalData)
	}

	var state [16]uint32
	setupState(&state, &c.key, nonce)

	ciphertext = ciphertext[:len(ciphertext)-16]
	ret, out := sliceForAppend(dst, len(ciphertext))
	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
		for i := range out {
			out[i] = 0
		}
		return nil, errOpen
	}

	return ret, nil
}
<commit_msg>chacha20poly1305: correct AVX2 feature detection<commit_after>// Copyright 2016 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.

// +build go1.7,amd64,!gccgo,!appengine

package chacha20poly1305

import (
	"encoding/binary"

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

//go:noescape
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool

//go:noescape
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)

var (
	useASM  = cpu.X86.HasSSSE3
	useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2
)

// setupState writes a ChaCha20 input matrix to state. See
// https://tools.ietf.org/html/rfc7539#section-2.3.
func setupState(state *[16]uint32, key *[8]uint32, nonce []byte) {
	state[0] = 0x61707865
	state[1] = 0x3320646e
	state[2] = 0x79622d32
	state[3] = 0x6b206574

	state[4] = key[0]
	state[5] = key[1]
	state[6] = key[2]
	state[7] = key[3]
	state[8] = key[4]
	state[9] = key[5]
	state[10] = key[6]
	state[11] = key[7]

	state[12] = 0
	state[13] = binary.LittleEndian.Uint32(nonce[:4])
	state[14] = binary.LittleEndian.Uint32(nonce[4:8])
	state[15] = binary.LittleEndian.Uint32(nonce[8:12])
}

func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
	if !useASM {
		return c.sealGeneric(dst, nonce, plaintext, additionalData)
	}

	var state [16]uint32
	setupState(&state, &c.key, nonce)

	ret, out := sliceForAppend(dst, len(plaintext)+16)
	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
	return ret
}

func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
	if !useASM {
		return c.openGeneric(dst, nonce, ciphertext, additionalData)
	}

	var state [16]uint32
	setupState(&state, &c.key, nonce)

	ciphertext = ciphertext[:len(ciphertext)-16]
	ret, out := sliceForAppend(dst, len(ciphertext))
	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
		for i := range out {
			out[i] = 0
		}
		return nil, errOpen
	}

	return ret, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify

import (
	"fmt"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/repr"
	"golang.org/x/net/context"
)

// Create a visitor for the DAG of blobs in the supplied bucket. Node names are
// expected to be generated by Node.String.
//
// The visitor reads directory blobs, parses them, and emits their children as
// adjacent nodes. For file nodes, the visitor verifies that their score exists
// (according to allScores), and verifies that the blob can be loaded if
// readFiles is true.
//
// If work is to be preserved across runs, knownStructure should be filled in
// with parenthood information from previously-generated records (for both file
// and directories). Nodes that exist as keys will not be re-verified, except
// to confirm that they still exist in allScores.
//
// A record is written to the supplied channel for every piece of information
// that is verified.
//
// It is expected that the blob store's Load method does score verification for
// us.
func NewVisitor(
	readFiles bool,
	allScores []blob.Score,
	knownStructure map[Node][]Node,
	records chan<- Record,
	clock timeutil.Clock,
	bs blob.Store) (v graph.Visitor) {
	typed := &visitor{
		readFiles:      readFiles,
		records:        records,
		clock:          clock,
		blobStore:      bs,
		knownScores:    make(map[blob.Score]struct{}),
		knownStructure: knownStructure,
	}

	for _, score := range allScores {
		typed.knownScores[score] = struct{}{}
	}

	v = typed
	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type visitor struct {
	readFiles      bool
	records        chan<- Record
	clock          timeutil.Clock
	blobStore      blob.Store
	knownScores    map[blob.Score]struct{}
	knownStructure map[Node][]Node
}

func (v *visitor) visitFile(
	ctx context.Context,
	n Node) (err error) {
	// If reading files is disabled, there is nothing further to do.
	if !v.readFiles {
		return
	}

	// Make sure we can load the blob contents. Presumably the blob store
	// verifies the score (of the ciphertext) on the way through.
	_, err = v.blobStore.Load(ctx, n.Score)
	if err != nil {
		err = fmt.Errorf("Load(%s): %v", n.Score.Hex(), err)
		return
	}

	// Certify that we verified the file piece.
	r := Record{
		Time: v.clock.Now(),
		Node: n,
	}

	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	return
}

func (v *visitor) visitDir(
	ctx context.Context,
	n Node) (adjacent []string, err error) {
	// Load the blob contents.
	contents, err := v.blobStore.Load(ctx, n.Score)
	if err != nil {
		err = fmt.Errorf("Load(%s): %v", n.Score.Hex(), err)
		return
	}

	// Parse the listing.
	listing, err := repr.UnmarshalDir(contents)
	if err != nil {
		err = fmt.Errorf("UnmarshalDir(%s): %v", n.Score.Hex(), err)
		return
	}

	// Build a record containing a child node for each score in each entry.
	r := Record{
		Time: v.clock.Now(),
		Node: n,
	}

	for _, entry := range listing {
		var n Node

		// Is this a directory?
		switch entry.Type {
		case fs.TypeFile:
			n.Dir = false

		case fs.TypeDirectory:
			n.Dir = true

		case fs.TypeSymlink:
			if len(entry.Scores) != 0 {
				err = fmt.Errorf(
					"Dir %s: symlink unexpectedly contains scores",
					n.Score.Hex())

				return
			}

		default:
			err = fmt.Errorf(
				"Dir %s: unknown entry type %v",
				n.Score.Hex(),
				entry.Type)

			return
		}

		// Add a node for each score.
		for _, score := range entry.Scores {
			n.Score = score
			r.Children = append(r.Children, n)
		}
	}

	// Certify that we verified the directory.
	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	// Return child node names.
	for _, child := range r.Children {
		adjacent = append(adjacent, child.String())
	}

	return
}

func (v *visitor) Visit(
	ctx context.Context,
	nodeName string) (adjacent []string, err error) {
	// Parse the node name.
	n, err := ParseNode(nodeName)
	if err != nil {
		err = fmt.Errorf("ParseNode(%q): %v", nodeName, err)
		return
	}

	// Make sure the score actually exists.
	if _, ok := v.knownScores[n.Score]; !ok {
		err = fmt.Errorf("Unknown score for node: %s", n.String())
		return
	}

	// If we have already verified this node, there is nothing further to do.
	if _, ok := v.knownStructure[n]; ok {
		return
	}

	// Perform file or directory-specific logic.
	if n.Dir {
		adjacent, err = v.visitDir(ctx, n)
		return
	} else {
		err = v.visitFile(ctx, n)
		return
	}
}
<commit_msg>Fixed some bugs.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify

import (
	"fmt"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/repr"
	"golang.org/x/net/context"
)

// Create a visitor for the DAG of blobs in the supplied bucket. Node names are
// expected to be generated by Node.String.
//
// The visitor reads directory blobs, parses them, and emits their children as
// adjacent nodes. For file nodes, the visitor verifies that their score exists
// (according to allScores), and verifies that the blob can be loaded if
// readFiles is true.
//
// If work is to be preserved across runs, knownStructure should be filled in
// with parenthood information from previously-generated records (for both file
// and directories). Nodes that exist as keys will not be re-verified, except
// to confirm that they still exist in allScores.
//
// A record is written to the supplied channel for every piece of information
// that is verified.
//
// It is expected that the blob store's Load method does score verification for
// us.
func NewVisitor(
	readFiles bool,
	allScores []blob.Score,
	knownStructure map[Node][]Node,
	records chan<- Record,
	clock timeutil.Clock,
	bs blob.Store) (v graph.Visitor) {
	typed := &visitor{
		readFiles:      readFiles,
		records:        records,
		clock:          clock,
		blobStore:      bs,
		knownScores:    make(map[blob.Score]struct{}),
		knownStructure: knownStructure,
	}

	for _, score := range allScores {
		typed.knownScores[score] = struct{}{}
	}

	v = typed
	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type visitor struct {
	readFiles      bool
	records        chan<- Record
	clock          timeutil.Clock
	blobStore      blob.Store
	knownScores    map[blob.Score]struct{}
	knownStructure map[Node][]Node
}

func (v *visitor) visitFile(
	ctx context.Context,
	n Node) (err error) {
	// If reading files is disabled, there is nothing further to do.
	if !v.readFiles {
		return
	}

	// Make sure we can load the blob contents. Presumably the blob store
	// verifies the score (of the ciphertext) on the way through.
	_, err = v.blobStore.Load(ctx, n.Score)
	if err != nil {
		err = fmt.Errorf("Load(%s): %v", n.Score.Hex(), err)
		return
	}

	// Certify that we verified the file piece.
	r := Record{
		Time: v.clock.Now(),
		Node: n,
	}

	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	return
}

func (v *visitor) visitDir(
	ctx context.Context,
	parent Node) (adjacent []string, err error) {
	// Load the blob contents.
	contents, err := v.blobStore.Load(ctx, parent.Score)
	if err != nil {
		err = fmt.Errorf("Load(%s): %v", parent.Score.Hex(), err)
		return
	}

	// Parse the listing.
	listing, err := repr.UnmarshalDir(contents)
	if err != nil {
		err = fmt.Errorf("UnmarshalDir(%s): %v", parent.Score.Hex(), err)
		return
	}

	// Build a record containing a child node for each score in each entry.
	r := Record{
		Time: v.clock.Now(),
		Node: parent,
	}

	for _, entry := range listing {
		var child Node

		// Is this a directory?
		switch entry.Type {
		case fs.TypeFile:
			child.Dir = false

		case fs.TypeDirectory:
			child.Dir = true

		case fs.TypeSymlink:
			if len(entry.Scores) != 0 {
				err = fmt.Errorf(
					"Dir %s: symlink unexpectedly contains scores",
					parent.Score.Hex())

				return
			}

		default:
			err = fmt.Errorf(
				"Dir %s: unknown entry type %v",
				parent.Score.Hex(),
				entry.Type)

			return
		}

		// Add a node for each score.
		for _, score := range entry.Scores {
			child.Score = score
			r.Children = append(r.Children, child)
		}
	}

	// Certify that we verified the directory.
	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	// Return child node names.
	for _, child := range r.Children {
		adjacent = append(adjacent, child.String())
	}

	return
}

func (v *visitor) Visit(
	ctx context.Context,
	nodeName string) (adjacent []string, err error) {
	// Parse the node name.
	n, err := ParseNode(nodeName)
	if err != nil {
		err = fmt.Errorf("ParseNode(%q): %v", nodeName, err)
		return
	}

	// Make sure the score actually exists.
	if _, ok := v.knownScores[n.Score]; !ok {
		err = fmt.Errorf("Unknown score for node: %s", n.String())
		return
	}

	// If we have already verified this node, there is nothing further to do.
	if _, ok := v.knownStructure[n]; ok {
		return
	}

	// Perform file or directory-specific logic.
	if n.Dir {
		adjacent, err = v.visitDir(ctx, n)
		return
	} else {
		err = v.visitFile(ctx, n)
		return
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 main

import (
	"testing"

	"k8s.io/client-go/1.4/pkg/api/v1"
)

type mockPodStore struct {
	f func() ([]v1.Pod, error)
}

func (ds mockPodStore) List() (pods []v1.Pod, err error) {
	return ds.f()
}

func TestPodCollector(t *testing.T) {
	// Fixed metadata on type and help text. We prepend this to every expected
	// output so we only have to modify a single place when doing adjustments.
	const metadata = `
		# HELP kube_pod_container_info Information about a container in a pod.
		# TYPE kube_pod_container_info gauge
		# HELP kube_pod_container_status_ready Describes whether the containers readiness check succeeded.
		# TYPE kube_pod_container_status_ready gauge
		# HELP kube_pod_container_status_restarts The number of container restarts per container.
		# TYPE kube_pod_container_status_restarts counter
		# HELP kube_pod_container_status_running Describes whether the container is currently in running state.
		# TYPE kube_pod_container_status_running gauge
		# HELP kube_pod_container_status_terminated Describes whether the container is currently in terminated state.
		# TYPE kube_pod_container_status_terminated gauge
		# HELP kube_pod_container_status_waiting Describes whether the container is currently in waiting state.
		# TYPE kube_pod_container_status_waiting gauge
		# HELP kube_pod_info Information about pod.
		# TYPE kube_pod_info gauge
		# HELP kube_pod_status_phase The pods current phase.
		# TYPE kube_pod_status_phase gauge
		# HELP kube_pod_status_ready Describes whether the pod is ready to serve requests.
		# TYPE kube_pod_status_ready gauge
	`
	cases := []struct {
		pods    []v1.Pod
		metrics []string
		want    string
	}{
		{
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:        "container1",
								Image:       "gcr.io/google_containers/hyperkube1",
								ImageID:     "docker://sha256:aaa",
								ContainerID: "docker://ab123",
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:        "container2",
								Image:       "gcr.io/google_containers/hyperkube2",
								ImageID:     "docker://sha256:bbb",
								ContainerID: "docker://cd456",
							},
							v1.ContainerStatus{
								Name:        "container3",
								Image:       "gcr.io/google_containers/hyperkube3",
								ImageID:     "docker://sha256:ccc",
								ContainerID: "docker://ef789",
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_info{container="container1",container_id="docker://ab123",image="gcr.io/google_containers/hyperkube1",image_id="docker://sha256:aaa",namespace="ns1",pod="pod1"} 1
				kube_pod_container_info{container="container2",container_id="docker://cd456",image="gcr.io/google_containers/hyperkube2",image_id="docker://sha256:bbb",namespace="ns2",pod="pod2"} 1
				kube_pod_container_info{container="container3",container_id="docker://ef789",image="gcr.io/google_containers/hyperkube3",image_id="docker://sha256:ccc",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_container_info"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:  "container1",
								Ready: true,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:  "container2",
								Ready: true,
							},
							v1.ContainerStatus{
								Name:  "container3",
								Ready: false,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_ready{container="container1",namespace="ns1",pod="pod1"} 1
				kube_pod_container_status_ready{container="container2",namespace="ns2",pod="pod2"} 1
				kube_pod_container_status_ready{container="container3",namespace="ns2",pod="pod2"} 0
				`,
			metrics: []string{"kube_pod_container_status_ready"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:         "container1",
								RestartCount: 0,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:         "container2",
								RestartCount: 0,
							},
							v1.ContainerStatus{
								Name:         "container3",
								RestartCount: 1,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_restarts{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_restarts{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_restarts{container="container3",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_container_status_restarts"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name: "container1",
								State: v1.ContainerState{
									Running: &v1.ContainerStateRunning{},
								},
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name: "container2",
								State: v1.ContainerState{
									Terminated: &v1.ContainerStateTerminated{},
								},
							},
							v1.ContainerStatus{
								Name: "container3",
								State: v1.ContainerState{
									Waiting: &v1.ContainerStateWaiting{},
								},
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_running{container="container1",namespace="ns1",pod="pod1"} 1
				kube_pod_container_status_running{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_running{container="container3",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_terminated{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_terminated{container="container2",namespace="ns2",pod="pod2"} 1
				kube_pod_container_status_terminated{container="container3",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_waiting{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_waiting{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_waiting{container="container3",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{
				"kube_pod_container_status_running",
				"kube_pod_container_status_waiting",
				"kube_pod_container_status_terminated",
			},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						HostIP: "1.1.1.1",
						PodIP:  "1.2.3.4",
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						HostIP: "1.1.1.1",
						PodIP:  "2.3.4.5",
					},
				},
			},
			want: metadata + `
				kube_pod_info{host_ip="1.1.1.1",namespace="ns1",pod="pod1",pod_ip="1.2.3.4"} 1
				kube_pod_info{host_ip="1.1.1.1",namespace="ns2",pod="pod2",pod_ip="2.3.4.5"} 1
				`,
			metrics: []string{"kube_pod_info"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						Phase: "Running",
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						Phase: "Pending",
					},
				},
			},
			want: metadata + `
				kube_pod_status_phase{namespace="ns1",phase="Running",pod="pod1"} 1
				kube_pod_status_phase{namespace="ns2",phase="Pending",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_status_phase"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodReady,
								Status: v1.ConditionTrue,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodReady,
								Status: v1.ConditionFalse,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_status_ready{condition="false",namespace="ns1",pod="pod1"} 0
				kube_pod_status_ready{condition="false",namespace="ns2",pod="pod2"} 1
				kube_pod_status_ready{condition="true",namespace="ns1",pod="pod1"} 1
				kube_pod_status_ready{condition="true",namespace="ns2",pod="pod2"} 0
				kube_pod_status_ready{condition="unknown",namespace="ns1",pod="pod1"} 0
				kube_pod_status_ready{condition="unknown",namespace="ns2",pod="pod2"} 0
			`,
			metrics: []string{"kube_pod_status_ready"},
		},
	}
	for _, c := range cases {
		pc := &podCollector{
			store: mockPodStore{
				f: func() ([]v1.Pod, error) { return c.pods, nil },
			},
		}
		if err := gatherAndCompare(pc, c.want, c.metrics); err != nil {
			t.Errorf("unexpected collecting result:\n%s", err)
		}
	}
}
<commit_msg>Add test for kube_pod_status_scheduled<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 main

import (
	"testing"

	"k8s.io/client-go/1.4/pkg/api/v1"
)

type mockPodStore struct {
	f func() ([]v1.Pod, error)
}

func (ds mockPodStore) List() (pods []v1.Pod, err error) {
	return ds.f()
}

func TestPodCollector(t *testing.T) {
	// Fixed metadata on type and help text. We prepend this to every expected
	// output so we only have to modify a single place when doing adjustments.
	const metadata = `
		# HELP kube_pod_container_info Information about a container in a pod.
		# TYPE kube_pod_container_info gauge
		# HELP kube_pod_container_status_ready Describes whether the containers readiness check succeeded.
		# TYPE kube_pod_container_status_ready gauge
		# HELP kube_pod_container_status_restarts The number of container restarts per container.
		# TYPE kube_pod_container_status_restarts counter
		# HELP kube_pod_container_status_running Describes whether the container is currently in running state.
		# TYPE kube_pod_container_status_running gauge
		# HELP kube_pod_container_status_terminated Describes whether the container is currently in terminated state.
		# TYPE kube_pod_container_status_terminated gauge
		# HELP kube_pod_container_status_waiting Describes whether the container is currently in waiting state.
		# TYPE kube_pod_container_status_waiting gauge
		# HELP kube_pod_info Information about pod.
		# TYPE kube_pod_info gauge
		# HELP kube_pod_status_phase The pods current phase.
		# TYPE kube_pod_status_phase gauge
		# HELP kube_pod_status_ready Describes whether the pod is ready to serve requests.
		# TYPE kube_pod_status_ready gauge
		# HELP kube_pod_status_scheduled Describes the status of the scheduling process for the pod.
		# TYPE kube_pod_status_scheduled gauge
	`
	cases := []struct {
		pods    []v1.Pod
		metrics []string
		want    string
	}{
		{
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:        "container1",
								Image:       "gcr.io/google_containers/hyperkube1",
								ImageID:     "docker://sha256:aaa",
								ContainerID: "docker://ab123",
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:        "container2",
								Image:       "gcr.io/google_containers/hyperkube2",
								ImageID:     "docker://sha256:bbb",
								ContainerID: "docker://cd456",
							},
							v1.ContainerStatus{
								Name:        "container3",
								Image:       "gcr.io/google_containers/hyperkube3",
								ImageID:     "docker://sha256:ccc",
								ContainerID: "docker://ef789",
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_info{container="container1",container_id="docker://ab123",image="gcr.io/google_containers/hyperkube1",image_id="docker://sha256:aaa",namespace="ns1",pod="pod1"} 1
				kube_pod_container_info{container="container2",container_id="docker://cd456",image="gcr.io/google_containers/hyperkube2",image_id="docker://sha256:bbb",namespace="ns2",pod="pod2"} 1
				kube_pod_container_info{container="container3",container_id="docker://ef789",image="gcr.io/google_containers/hyperkube3",image_id="docker://sha256:ccc",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_container_info"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:  "container1",
								Ready: true,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:  "container2",
								Ready: true,
							},
							v1.ContainerStatus{
								Name:  "container3",
								Ready: false,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_ready{container="container1",namespace="ns1",pod="pod1"} 1
				kube_pod_container_status_ready{container="container2",namespace="ns2",pod="pod2"} 1
				kube_pod_container_status_ready{container="container3",namespace="ns2",pod="pod2"} 0
				`,
			metrics: []string{"kube_pod_container_status_ready"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:         "container1",
								RestartCount: 0,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name:         "container2",
								RestartCount: 0,
							},
							v1.ContainerStatus{
								Name:         "container3",
								RestartCount: 1,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_restarts{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_restarts{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_restarts{container="container3",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_container_status_restarts"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name: "container1",
								State: v1.ContainerState{
									Running: &v1.ContainerStateRunning{},
								},
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						ContainerStatuses: []v1.ContainerStatus{
							v1.ContainerStatus{
								Name: "container2",
								State: v1.ContainerState{
									Terminated: &v1.ContainerStateTerminated{},
								},
							},
							v1.ContainerStatus{
								Name: "container3",
								State: v1.ContainerState{
									Waiting: &v1.ContainerStateWaiting{},
								},
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_container_status_running{container="container1",namespace="ns1",pod="pod1"} 1
				kube_pod_container_status_running{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_running{container="container3",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_terminated{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_terminated{container="container2",namespace="ns2",pod="pod2"} 1
				kube_pod_container_status_terminated{container="container3",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_waiting{container="container1",namespace="ns1",pod="pod1"} 0
				kube_pod_container_status_waiting{container="container2",namespace="ns2",pod="pod2"} 0
				kube_pod_container_status_waiting{container="container3",namespace="ns2",pod="pod2"} 1
				`,
			metrics: []string{
				"kube_pod_container_status_running",
				"kube_pod_container_status_waiting",
				"kube_pod_container_status_terminated",
			},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						HostIP: "1.1.1.1",
						PodIP:  "1.2.3.4",
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						HostIP: "1.1.1.1",
						PodIP:  "2.3.4.5",
					},
				},
			},
			want: metadata + `
				kube_pod_info{host_ip="1.1.1.1",namespace="ns1",pod="pod1",pod_ip="1.2.3.4"} 1
				kube_pod_info{host_ip="1.1.1.1",namespace="ns2",pod="pod2",pod_ip="2.3.4.5"} 1
				`,
			metrics: []string{"kube_pod_info"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						Phase: "Running",
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						Phase: "Pending",
					},
				},
			},
			want: metadata + `
				kube_pod_status_phase{namespace="ns1",phase="Running",pod="pod1"} 1
				kube_pod_status_phase{namespace="ns2",phase="Pending",pod="pod2"} 1
				`,
			metrics: []string{"kube_pod_status_phase"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodReady,
								Status: v1.ConditionTrue,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodReady,
								Status: v1.ConditionFalse,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_status_ready{condition="false",namespace="ns1",pod="pod1"} 0
				kube_pod_status_ready{condition="false",namespace="ns2",pod="pod2"} 1
				kube_pod_status_ready{condition="true",namespace="ns1",pod="pod1"} 1
				kube_pod_status_ready{condition="true",namespace="ns2",pod="pod2"} 0
				kube_pod_status_ready{condition="unknown",namespace="ns1",pod="pod1"} 0
				kube_pod_status_ready{condition="unknown",namespace="ns2",pod="pod2"} 0
			`,
			metrics: []string{"kube_pod_status_ready"},
		}, {
			pods: []v1.Pod{
				{
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod1",
						Namespace: "ns1",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodScheduled,
								Status: v1.ConditionTrue,
							},
						},
					},
				}, {
					ObjectMeta: v1.ObjectMeta{
						Name:      "pod2",
						Namespace: "ns2",
					},
					Status: v1.PodStatus{
						Conditions: []v1.PodCondition{
							v1.PodCondition{
								Type:   v1.PodScheduled,
								Status: v1.ConditionFalse,
							},
						},
					},
				},
			},
			want: metadata + `
				kube_pod_status_scheduled{condition="false",namespace="ns1",pod="pod1"} 0
				kube_pod_status_scheduled{condition="false",namespace="ns2",pod="pod2"} 1
				kube_pod_status_scheduled{condition="true",namespace="ns1",pod="pod1"} 1
				kube_pod_status_scheduled{condition="true",namespace="ns2",pod="pod2"} 0
				kube_pod_status_scheduled{condition="unknown",namespace="ns1",pod="pod1"} 0
				kube_pod_status_scheduled{condition="unknown",namespace="ns2",pod="pod2"} 0
			`,
			metrics: []string{"kube_pod_status_scheduled"},
		},
	}
	for _, c := range cases {
		pc := &podCollector{
			store: mockPodStore{
				f: func() ([]v1.Pod, error) { return c.pods, nil },
			},
		}
		if err := gatherAndCompare(pc, c.want, c.metrics); err != nil {
			t.Errorf("unexpected collecting result:\n%s", err)
		}
	}
}
<|endoftext|>


<commit_before>package buildah

import (
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/containers/buildah/util"
	"github.com/containers/libpod/pkg/chrootuser"
	"github.com/containers/storage/pkg/archive"
	"github.com/containers/storage/pkg/idtools"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// AddAndCopyOptions holds options for add and copy commands.
type AddAndCopyOptions struct {
	// Chown is a spec for the user who should be given ownership over the
	// newly-added content, potentially overriding permissions which would
	// otherwise match those of local files and directories being copied.
	Chown string
	// All of the data being copied will pass through Hasher, if set.
	// If the sources are URLs or files, their contents will be passed to
	// Hasher.
	// If the sources include directory trees, Hasher will be passed
	// tar-format archives of the directory trees.
	Hasher io.Writer
	// Exludes contents in the .dockerignore file
	Excludes []string
	// current directory on host
	ContextDir string
}

// addURL copies the contents of the source URL to the destination.  This is
// its own function so that deferred closes happen after we're done pulling
// down each item of potentially many.
func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer) error {
	logrus.Debugf("saving %q to %q", srcurl, destination)
	resp, err := http.Get(srcurl)
	if err != nil {
		return errors.Wrapf(err, "error getting %q", srcurl)
	}
	defer resp.Body.Close()
	f, err := os.Create(destination)
	if err != nil {
		return errors.Wrapf(err, "error creating %q", destination)
	}
	if err = f.Chown(owner.UID, owner.GID); err != nil {
		return errors.Wrapf(err, "error setting owner of %q to %d:%d", destination, owner.UID, owner.GID)
	}
	if last := resp.Header.Get("Last-Modified"); last != "" {
		if mtime, err2 := time.Parse(time.RFC1123, last); err2 != nil {
			logrus.Debugf("error parsing Last-Modified time %q: %v", last, err2)
		} else {
			defer func() {
				if err3 := os.Chtimes(destination, time.Now(), mtime); err3 != nil {
					logrus.Debugf("error setting mtime on %q to Last-Modified time %q: %v", destination, last, err3)
				}
			}()
		}
	}
	defer f.Close()
	bodyReader := io.Reader(resp.Body)
	if hasher != nil {
		bodyReader = io.TeeReader(bodyReader, hasher)
	}
	n, err := io.Copy(f, bodyReader)
	if err != nil {
		return errors.Wrapf(err, "error reading contents for %q from %q", destination, srcurl)
	}
	if resp.ContentLength >= 0 && n != resp.ContentLength {
		return errors.Errorf("error reading contents for %q from %q: wrong length (%d != %d)", destination, srcurl, n, resp.ContentLength)
	}
	if err := f.Chmod(0600); err != nil {
		return errors.Wrapf(err, "error setting permissions on %q", destination)
	}
	return nil
}

// Add copies the contents of the specified sources into the container's root
// filesystem, optionally extracting contents of local files that look like
// non-empty archives.
func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, source ...string) error {
	excludes := DockerIgnoreHelper(options.Excludes, options.ContextDir)
	mountPoint, err := b.Mount(b.MountLabel)
	if err != nil {
		return err
	}
	defer func() {
		if err2 := b.Unmount(); err2 != nil {
			logrus.Errorf("error unmounting container: %v", err2)
		}
	}()
	// Find out which user (and group) the destination should belong to.
	user, err := b.user(mountPoint, options.Chown)
	if err != nil {
		return err
	}
	containerOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
	hostUID, hostGID, err := util.GetHostIDs(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap, user.UID, user.GID)
	if err != nil {
		return err
	}
	hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
	dest := mountPoint
	if destination != "" && filepath.IsAbs(destination) {
		dest = filepath.Join(dest, destination)
	} else {
		if err = idtools.MkdirAllAndChownNew(filepath.Join(dest, b.WorkDir()), 0755, hostOwner); err != nil {
			return errors.Wrapf(err, "error creating directory %q", filepath.Join(dest, b.WorkDir()))
		}
		dest = filepath.Join(dest, b.WorkDir(), destination)
	}
	// If the destination was explicitly marked as a directory by ending it
	// with a '/', create it so that we can be sure that it's a directory,
	// and any files we're copying will be placed in the directory.
	if len(destination) > 0 && destination[len(destination)-1] == os.PathSeparator {
		if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
			return errors.Wrapf(err, "error creating directory %q", dest)
		}
	}
	// Make sure the destination's parent directory is usable.
	if destpfi, err2 := os.Stat(filepath.Dir(dest)); err2 == nil && !destpfi.IsDir() {
		return errors.Errorf("%q already exists, but is not a subdirectory)", filepath.Dir(dest))
	}
	// Now look at the destination itself.
	destfi, err := os.Stat(dest)
	if err != nil {
		if !os.IsNotExist(err) {
			return errors.Wrapf(err, "couldn't determine what %q is", dest)
		}
		destfi = nil
	}
	if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
		return errors.Errorf("destination %q is not a directory", dest)
	}
	copyFileWithTar := b.copyFileWithTar(&containerOwner, options.Hasher)
	copyWithTar := b.copyWithTar(&containerOwner, options.Hasher)
	untarPath := b.untarPath(nil, options.Hasher)
	err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
	if err != nil {
		return err
	}
	return nil
}

// user returns the user (and group) information which the destination should belong to.
func (b *Builder) user(mountPoint string, userspec string) (specs.User, error) {
	if userspec == "" {
		userspec = b.User()
	}

	uid, gid, err := chrootuser.GetUser(mountPoint, userspec)
	u := specs.User{
		UID:      uid,
		GID:      gid,
		Username: userspec,
	}
	if !strings.Contains(userspec, ":") {
		groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
		if err2 != nil {
			if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil {
				err = err2
			}
		} else {
			u.AdditionalGids = groups
		}

	}
	return u, err
}

// DockerIgnore struct keep info from .dockerignore
type DockerIgnore struct {
	excludePath string
	isExcluded  bool
}

// DockerIgnoreHelper returns the lines from .dockerignore file without the comments
// and reverses the order
func DockerIgnoreHelper(lines []string, contextDir string) []DockerIgnore {
	var excludes []DockerIgnore
	// the last match of a file in the .dockerignmatches determines whether it is included or excluded
	// reverse the order
	for i := len(lines) - 1; i >= 0; i-- {
		exclude := lines[i]
		// ignore the comment in .dockerignore
		if strings.HasPrefix(exclude, "#") || len(exclude) == 0 {
			continue
		}
		excludeFlag := true
		if strings.HasPrefix(exclude, "!") {
			exclude = strings.TrimPrefix(exclude, "!")
			excludeFlag = false
		}
		excludes = append(excludes, DockerIgnore{excludePath: filepath.Join(contextDir, exclude), isExcluded: excludeFlag})
	}
	if len(excludes) != 0 {
		excludes = append(excludes, DockerIgnore{excludePath: filepath.Join(contextDir, ".dockerignore"), isExcluded: true})
	}
	return excludes
}

func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error {
	for _, src := range source {
		if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
			// We assume that source is a file, and we're copying
			// it to the destination.  If the destination is
			// already a directory, create a file inside of it.
			// Otherwise, the destination is the file to which
			// we'll save the contents.
			url, err := url.Parse(src)
			if err != nil {
				return errors.Wrapf(err, "error parsing URL %q", src)
			}
			d := dest
			if destfi != nil && destfi.IsDir() {
				d = filepath.Join(dest, path.Base(url.Path))
			}
			if err = addURL(d, src, hostOwner, options.Hasher); err != nil {
				return err
			}
			continue
		}

		glob, err := filepath.Glob(src)
		if err != nil {
			return errors.Wrapf(err, "invalid glob %q", src)
		}
		if len(glob) == 0 {
			return errors.Wrapf(syscall.ENOENT, "no files found matching %q", src)
		}
	outer:
		for _, gsrc := range glob {
			esrc, err := filepath.EvalSymlinks(gsrc)
			if err != nil {
				return errors.Wrapf(err, "error evaluating symlinks %q", gsrc)
			}
			srcfi, err := os.Stat(esrc)
			if err != nil {
				return errors.Wrapf(err, "error reading %q", esrc)
			}
			if srcfi.IsDir() {
				// The source is a directory, so copy the contents of
				// the source directory into the target directory.  Try
				// to create it first, so that if there's a problem,
				// we'll discover why that won't work.
				if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
					return errors.Wrapf(err, "error creating directory %q", dest)
				}
				logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
				if len(excludes) == 0 {
					if err = copyWithTar(esrc, dest); err != nil {
						return errors.Wrapf(err, "error copying %q to %q", esrc, dest)
					}
					continue
				}
				err := filepath.Walk(esrc, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if info.IsDir() {
						return nil
					}
					for _, exclude := range excludes {
						match, err := filepath.Match(filepath.Clean(exclude.excludePath), filepath.Clean(path))
						if err != nil {
							return err
						}
						if !match {
							continue
						}
						if exclude.isExcluded {
							return nil
						}
						break
					}
					// combine the filename with the dest directory
					fpath := strings.TrimPrefix(path, options.ContextDir)
					if err = copyFileWithTar(path, filepath.Join(dest, fpath)); err != nil {
						return errors.Wrapf(err, "error copying %q to %q", path, dest)
					}
					return nil
				})
				if err != nil {
					return err
				}
				continue
			}

			for _, exclude := range excludes {
				match, err := filepath.Match(filepath.Clean(exclude.excludePath), esrc)
				if err != nil {
					return err
				}
				if !match {
					continue
				}
				if exclude.isExcluded {
					continue outer
				}
				break
			}

			if !extract || !archive.IsArchivePath(esrc) {
				// This source is a file, and either it's not an
				// archive, or we don't care whether or not it's an
				// archive.
				d := dest
				if destfi != nil && destfi.IsDir() {
					d = filepath.Join(dest, filepath.Base(gsrc))
				}
				// Copy the file, preserving attributes.
				logrus.Debugf("copying %q to %q", esrc, d)
				if err = copyFileWithTar(esrc, d); err != nil {
					return errors.Wrapf(err, "error copying %q to %q", esrc, d)
				}
				continue
			}
			// We're extracting an archive into the destination directory.
			logrus.Debugf("extracting contents of %q into %q", esrc, dest)
			if err = untarPath(esrc, dest); err != nil {
				return errors.Wrapf(err, "error extracting %q into %q", esrc, dest)
			}
		}
	}
	return nil
}
<commit_msg>Export fields of the DokcerIgnore struct<commit_after>package buildah

import (
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/containers/buildah/util"
	"github.com/containers/libpod/pkg/chrootuser"
	"github.com/containers/storage/pkg/archive"
	"github.com/containers/storage/pkg/idtools"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// AddAndCopyOptions holds options for add and copy commands.
type AddAndCopyOptions struct {
	// Chown is a spec for the user who should be given ownership over the
	// newly-added content, potentially overriding permissions which would
	// otherwise match those of local files and directories being copied.
	Chown string
	// All of the data being copied will pass through Hasher, if set.
	// If the sources are URLs or files, their contents will be passed to
	// Hasher.
	// If the sources include directory trees, Hasher will be passed
	// tar-format archives of the directory trees.
	Hasher io.Writer
	// Exludes contents in the .dockerignore file
	Excludes []string
	// current directory on host
	ContextDir string
}

// addURL copies the contents of the source URL to the destination.  This is
// its own function so that deferred closes happen after we're done pulling
// down each item of potentially many.
func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer) error {
	logrus.Debugf("saving %q to %q", srcurl, destination)
	resp, err := http.Get(srcurl)
	if err != nil {
		return errors.Wrapf(err, "error getting %q", srcurl)
	}
	defer resp.Body.Close()
	f, err := os.Create(destination)
	if err != nil {
		return errors.Wrapf(err, "error creating %q", destination)
	}
	if err = f.Chown(owner.UID, owner.GID); err != nil {
		return errors.Wrapf(err, "error setting owner of %q to %d:%d", destination, owner.UID, owner.GID)
	}
	if last := resp.Header.Get("Last-Modified"); last != "" {
		if mtime, err2 := time.Parse(time.RFC1123, last); err2 != nil {
			logrus.Debugf("error parsing Last-Modified time %q: %v", last, err2)
		} else {
			defer func() {
				if err3 := os.Chtimes(destination, time.Now(), mtime); err3 != nil {
					logrus.Debugf("error setting mtime on %q to Last-Modified time %q: %v", destination, last, err3)
				}
			}()
		}
	}
	defer f.Close()
	bodyReader := io.Reader(resp.Body)
	if hasher != nil {
		bodyReader = io.TeeReader(bodyReader, hasher)
	}
	n, err := io.Copy(f, bodyReader)
	if err != nil {
		return errors.Wrapf(err, "error reading contents for %q from %q", destination, srcurl)
	}
	if resp.ContentLength >= 0 && n != resp.ContentLength {
		return errors.Errorf("error reading contents for %q from %q: wrong length (%d != %d)", destination, srcurl, n, resp.ContentLength)
	}
	if err := f.Chmod(0600); err != nil {
		return errors.Wrapf(err, "error setting permissions on %q", destination)
	}
	return nil
}

// Add copies the contents of the specified sources into the container's root
// filesystem, optionally extracting contents of local files that look like
// non-empty archives.
func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, source ...string) error {
	excludes := DockerIgnoreHelper(options.Excludes, options.ContextDir)
	mountPoint, err := b.Mount(b.MountLabel)
	if err != nil {
		return err
	}
	defer func() {
		if err2 := b.Unmount(); err2 != nil {
			logrus.Errorf("error unmounting container: %v", err2)
		}
	}()
	// Find out which user (and group) the destination should belong to.
	user, err := b.user(mountPoint, options.Chown)
	if err != nil {
		return err
	}
	containerOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
	hostUID, hostGID, err := util.GetHostIDs(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap, user.UID, user.GID)
	if err != nil {
		return err
	}
	hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
	dest := mountPoint
	if destination != "" && filepath.IsAbs(destination) {
		dest = filepath.Join(dest, destination)
	} else {
		if err = idtools.MkdirAllAndChownNew(filepath.Join(dest, b.WorkDir()), 0755, hostOwner); err != nil {
			return errors.Wrapf(err, "error creating directory %q", filepath.Join(dest, b.WorkDir()))
		}
		dest = filepath.Join(dest, b.WorkDir(), destination)
	}
	// If the destination was explicitly marked as a directory by ending it
	// with a '/', create it so that we can be sure that it's a directory,
	// and any files we're copying will be placed in the directory.
	if len(destination) > 0 && destination[len(destination)-1] == os.PathSeparator {
		if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
			return errors.Wrapf(err, "error creating directory %q", dest)
		}
	}
	// Make sure the destination's parent directory is usable.
	if destpfi, err2 := os.Stat(filepath.Dir(dest)); err2 == nil && !destpfi.IsDir() {
		return errors.Errorf("%q already exists, but is not a subdirectory)", filepath.Dir(dest))
	}
	// Now look at the destination itself.
	destfi, err := os.Stat(dest)
	if err != nil {
		if !os.IsNotExist(err) {
			return errors.Wrapf(err, "couldn't determine what %q is", dest)
		}
		destfi = nil
	}
	if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
		return errors.Errorf("destination %q is not a directory", dest)
	}
	copyFileWithTar := b.copyFileWithTar(&containerOwner, options.Hasher)
	copyWithTar := b.copyWithTar(&containerOwner, options.Hasher)
	untarPath := b.untarPath(nil, options.Hasher)
	err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
	if err != nil {
		return err
	}
	return nil
}

// user returns the user (and group) information which the destination should belong to.
func (b *Builder) user(mountPoint string, userspec string) (specs.User, error) {
	if userspec == "" {
		userspec = b.User()
	}

	uid, gid, err := chrootuser.GetUser(mountPoint, userspec)
	u := specs.User{
		UID:      uid,
		GID:      gid,
		Username: userspec,
	}
	if !strings.Contains(userspec, ":") {
		groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
		if err2 != nil {
			if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil {
				err = err2
			}
		} else {
			u.AdditionalGids = groups
		}

	}
	return u, err
}

// DockerIgnore struct keep info from .dockerignore
type DockerIgnore struct {
	ExcludePath string
	IsExcluded  bool
}

// DockerIgnoreHelper returns the lines from .dockerignore file without the comments
// and reverses the order
func DockerIgnoreHelper(lines []string, contextDir string) []DockerIgnore {
	var excludes []DockerIgnore
	// the last match of a file in the .dockerignmatches determines whether it is included or excluded
	// reverse the order
	for i := len(lines) - 1; i >= 0; i-- {
		exclude := lines[i]
		// ignore the comment in .dockerignore
		if strings.HasPrefix(exclude, "#") || len(exclude) == 0 {
			continue
		}
		excludeFlag := true
		if strings.HasPrefix(exclude, "!") {
			exclude = strings.TrimPrefix(exclude, "!")
			excludeFlag = false
		}
		excludes = append(excludes, DockerIgnore{ExcludePath: filepath.Join(contextDir, exclude), IsExcluded: excludeFlag})
	}
	if len(excludes) != 0 {
		excludes = append(excludes, DockerIgnore{ExcludePath: filepath.Join(contextDir, ".dockerignore"), IsExcluded: true})
	}
	return excludes
}

func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error {
	for _, src := range source {
		if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
			// We assume that source is a file, and we're copying
			// it to the destination.  If the destination is
			// already a directory, create a file inside of it.
			// Otherwise, the destination is the file to which
			// we'll save the contents.
			url, err := url.Parse(src)
			if err != nil {
				return errors.Wrapf(err, "error parsing URL %q", src)
			}
			d := dest
			if destfi != nil && destfi.IsDir() {
				d = filepath.Join(dest, path.Base(url.Path))
			}
			if err = addURL(d, src, hostOwner, options.Hasher); err != nil {
				return err
			}
			continue
		}

		glob, err := filepath.Glob(src)
		if err != nil {
			return errors.Wrapf(err, "invalid glob %q", src)
		}
		if len(glob) == 0 {
			return errors.Wrapf(syscall.ENOENT, "no files found matching %q", src)
		}
	outer:
		for _, gsrc := range glob {
			esrc, err := filepath.EvalSymlinks(gsrc)
			if err != nil {
				return errors.Wrapf(err, "error evaluating symlinks %q", gsrc)
			}
			srcfi, err := os.Stat(esrc)
			if err != nil {
				return errors.Wrapf(err, "error reading %q", esrc)
			}
			if srcfi.IsDir() {
				// The source is a directory, so copy the contents of
				// the source directory into the target directory.  Try
				// to create it first, so that if there's a problem,
				// we'll discover why that won't work.
				if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
					return errors.Wrapf(err, "error creating directory %q", dest)
				}
				logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
				if len(excludes) == 0 {
					if err = copyWithTar(esrc, dest); err != nil {
						return errors.Wrapf(err, "error copying %q to %q", esrc, dest)
					}
					continue
				}
				err := filepath.Walk(esrc, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if info.IsDir() {
						return nil
					}
					for _, exclude := range excludes {
						match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), filepath.Clean(path))
						if err != nil {
							return err
						}
						if !match {
							continue
						}
						if exclude.IsExcluded {
							return nil
						}
						break
					}
					// combine the filename with the dest directory
					fpath := strings.TrimPrefix(path, options.ContextDir)
					if err = copyFileWithTar(path, filepath.Join(dest, fpath)); err != nil {
						return errors.Wrapf(err, "error copying %q to %q", path, dest)
					}
					return nil
				})
				if err != nil {
					return err
				}
				continue
			}

			for _, exclude := range excludes {
				match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), esrc)
				if err != nil {
					return err
				}
				if !match {
					continue
				}
				if exclude.IsExcluded {
					continue outer
				}
				break
			}

			if !extract || !archive.IsArchivePath(esrc) {
				// This source is a file, and either it's not an
				// archive, or we don't care whether or not it's an
				// archive.
				d := dest
				if destfi != nil && destfi.IsDir() {
					d = filepath.Join(dest, filepath.Base(gsrc))
				}
				// Copy the file, preserving attributes.
				logrus.Debugf("copying %q to %q", esrc, d)
				if err = copyFileWithTar(esrc, d); err != nil {
					return errors.Wrapf(err, "error copying %q to %q", esrc, d)
				}
				continue
			}
			// We're extracting an archive into the destination directory.
			logrus.Debugf("extracting contents of %q into %q", esrc, dest)
			if err = untarPath(esrc, dest); err != nil {
				return errors.Wrapf(err, "error extracting %q into %q", esrc, dest)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package dkvolume

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
)

const (
	// DefaultDockerRootDirectory is the default directory where volumes will be created.
	DefaultDockerRootDirectory = "/var/lib/docker/volumes"

	defaultContentTypeV1_1        = "application/vnd.docker.plugins.v1.1+json"
	defaultImplementationManifest = `{"Implements": ["VolumeDriver"]}`
	pluginSpecDir                 = "/etc/docker/plugins"
	pluginSockDir                 = "/run/docker/plugins"

	activatePath    = "/Plugin.Activate"
	createPath      = "/VolumeDriver.Create"
	remotePath      = "/VolumeDriver.Remove"
	hostVirtualPath = "/VolumeDriver.Path"
	mountPath       = "/VolumeDriver.Mount"
	unmountPath     = "/VolumeDriver.Unmount"
)

// Request is the structure that docker's requests are deserialized to.
type Request struct {
	Name    string
	Options map[string]string `json:"Opts,omitempty"`
}

// Response is the strucutre that the plugin's responses are serialized to.
type Response struct {
	Mountpoint string
	Err        string
}

// Driver represent the interface a driver must fulfill.
type Driver interface {
	Create(Request) Response
	Remove(Request) Response
	Path(Request) Response
	Mount(Request) Response
	Unmount(Request) Response
}

// Handler forwards requests and responses between the docker daemon and the plugin.
type Handler struct {
	driver Driver
	mux    *http.ServeMux
}

type actionHandler func(Request) Response

// NewHandler initializes the request handler with a driver implementation.
func NewHandler(driver Driver) *Handler {
	h := &Handler{driver, http.NewServeMux()}
	h.initMux()
	return h
}

func (h *Handler) initMux() {
	h.mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", defaultContentTypeV1_1)
		fmt.Fprintln(w, defaultImplementationManifest)
	})

	h.handle(createPath, func(req Request) Response {
		return h.driver.Create(req)
	})

	h.handle(remotePath, func(req Request) Response {
		return h.driver.Remove(req)
	})

	h.handle(hostVirtualPath, func(req Request) Response {
		return h.driver.Path(req)
	})

	h.handle(mountPath, func(req Request) Response {
		return h.driver.Mount(req)
	})

	h.handle(unmountPath, func(req Request) Response {
		return h.driver.Unmount(req)
	})
}

func (h *Handler) handle(name string, actionCall actionHandler) {
	h.mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
		req, err := decodeRequest(w, r)
		if err != nil {
			return
		}

		res := actionCall(req)

		encodeResponse(w, res)
	})
}

// ServeTCP makes the handler to listen for request in a given TCP address.
// It also writes the spec file on the right directory for docker to read.
func (h *Handler) ServeTCP(pluginName, addr string) error {
	return h.listenAndServe("tcp", addr, pluginName)
}

// ServeUnix makes the handler to listen for requests in a unix socket.
// It also creates the socket file on the right directory for docker to read.
func (h *Handler) ServeUnix(systemGroup, addr string) error {
	return h.listenAndServe("unix", addr, systemGroup)
}

func (h *Handler) listenAndServe(proto, addr, group string) error {
	var (
		start = make(chan struct{})
		l     net.Listener
		err   error
		spec  string
	)

	server := http.Server{
		Addr:    addr,
		Handler: h.mux,
	}

	switch proto {
	case "tcp":
		l, err = newTCPSocket(addr, nil, start)
		if err == nil {
			spec, err = writeSpec(group, l.Addr().String())
		}
	case "unix":
		spec, err = fullSocketAddr(addr)
		if err == nil {
			l, err = newUnixSocket(spec, group, start)
		}
	}

	if spec != "" {
		defer os.Remove(spec)
	}
	if err != nil {
		return err
	}

	close(start)
	return server.Serve(l)
}

func decodeRequest(w http.ResponseWriter, r *http.Request) (req Request, err error) {
	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	return
}

func encodeResponse(w http.ResponseWriter, res Response) {
	w.Header().Set("Content-Type", defaultContentTypeV1_1)
	if res.Err != "" {
		w.WriteHeader(http.StatusInternalServerError)
	}
	json.NewEncoder(w).Encode(res)
}

func writeSpec(name, addr string) (string, error) {
	if err := os.MkdirAll(pluginSpecDir, 0755); err != nil {
		return "", err
	}

	spec := filepath.Join(pluginSpecDir, name+".spec")
	url := "tcp://" + addr
	if err := ioutil.WriteFile(spec, []byte(url), 0644); err != nil {
		return "", err
	}
	return spec, nil
}

func fullSocketAddr(addr string) (string, error) {
	if err := os.MkdirAll(pluginSockDir, 0755); err != nil {
		return "", err
	}

	if filepath.IsAbs(addr) {
		return addr, nil
	}

	return filepath.Join(pluginSockDir, addr+".sock"), nil
}
<commit_msg>Move volumes outside docker's controlled directory.<commit_after>package dkvolume

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
)

const (
	// DefaultDockerRootDirectory is the default directory where volumes will be created.
	DefaultDockerRootDirectory = "/var/lib/docker-volumes"

	defaultContentTypeV1_1        = "application/vnd.docker.plugins.v1.1+json"
	defaultImplementationManifest = `{"Implements": ["VolumeDriver"]}`
	pluginSpecDir                 = "/etc/docker/plugins"
	pluginSockDir                 = "/run/docker/plugins"

	activatePath    = "/Plugin.Activate"
	createPath      = "/VolumeDriver.Create"
	remotePath      = "/VolumeDriver.Remove"
	hostVirtualPath = "/VolumeDriver.Path"
	mountPath       = "/VolumeDriver.Mount"
	unmountPath     = "/VolumeDriver.Unmount"
)

// Request is the structure that docker's requests are deserialized to.
type Request struct {
	Name    string
	Options map[string]string `json:"Opts,omitempty"`
}

// Response is the strucutre that the plugin's responses are serialized to.
type Response struct {
	Mountpoint string
	Err        string
}

// Driver represent the interface a driver must fulfill.
type Driver interface {
	Create(Request) Response
	Remove(Request) Response
	Path(Request) Response
	Mount(Request) Response
	Unmount(Request) Response
}

// Handler forwards requests and responses between the docker daemon and the plugin.
type Handler struct {
	driver Driver
	mux    *http.ServeMux
}

type actionHandler func(Request) Response

// NewHandler initializes the request handler with a driver implementation.
func NewHandler(driver Driver) *Handler {
	h := &Handler{driver, http.NewServeMux()}
	h.initMux()
	return h
}

func (h *Handler) initMux() {
	h.mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", defaultContentTypeV1_1)
		fmt.Fprintln(w, defaultImplementationManifest)
	})

	h.handle(createPath, func(req Request) Response {
		return h.driver.Create(req)
	})

	h.handle(remotePath, func(req Request) Response {
		return h.driver.Remove(req)
	})

	h.handle(hostVirtualPath, func(req Request) Response {
		return h.driver.Path(req)
	})

	h.handle(mountPath, func(req Request) Response {
		return h.driver.Mount(req)
	})

	h.handle(unmountPath, func(req Request) Response {
		return h.driver.Unmount(req)
	})
}

func (h *Handler) handle(name string, actionCall actionHandler) {
	h.mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
		req, err := decodeRequest(w, r)
		if err != nil {
			return
		}

		res := actionCall(req)

		encodeResponse(w, res)
	})
}

// ServeTCP makes the handler to listen for request in a given TCP address.
// It also writes the spec file on the right directory for docker to read.
func (h *Handler) ServeTCP(pluginName, addr string) error {
	return h.listenAndServe("tcp", addr, pluginName)
}

// ServeUnix makes the handler to listen for requests in a unix socket.
// It also creates the socket file on the right directory for docker to read.
func (h *Handler) ServeUnix(systemGroup, addr string) error {
	return h.listenAndServe("unix", addr, systemGroup)
}

func (h *Handler) listenAndServe(proto, addr, group string) error {
	var (
		start = make(chan struct{})
		l     net.Listener
		err   error
		spec  string
	)

	server := http.Server{
		Addr:    addr,
		Handler: h.mux,
	}

	switch proto {
	case "tcp":
		l, err = newTCPSocket(addr, nil, start)
		if err == nil {
			spec, err = writeSpec(group, l.Addr().String())
		}
	case "unix":
		spec, err = fullSocketAddr(addr)
		if err == nil {
			l, err = newUnixSocket(spec, group, start)
		}
	}

	if spec != "" {
		defer os.Remove(spec)
	}
	if err != nil {
		return err
	}

	close(start)
	return server.Serve(l)
}

func decodeRequest(w http.ResponseWriter, r *http.Request) (req Request, err error) {
	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	return
}

func encodeResponse(w http.ResponseWriter, res Response) {
	w.Header().Set("Content-Type", defaultContentTypeV1_1)
	if res.Err != "" {
		w.WriteHeader(http.StatusInternalServerError)
	}
	json.NewEncoder(w).Encode(res)
}

func writeSpec(name, addr string) (string, error) {
	if err := os.MkdirAll(pluginSpecDir, 0755); err != nil {
		return "", err
	}

	spec := filepath.Join(pluginSpecDir, name+".spec")
	url := "tcp://" + addr
	if err := ioutil.WriteFile(spec, []byte(url), 0644); err != nil {
		return "", err
	}
	return spec, nil
}

func fullSocketAddr(addr string) (string, error) {
	if err := os.MkdirAll(pluginSockDir, 0755); err != nil {
		return "", err
	}

	if filepath.IsAbs(addr) {
		return addr, nil
	}

	return filepath.Join(pluginSockDir, addr+".sock"), nil
}
<|endoftext|>


<commit_before><commit_msg>Allow the Link and Content-Length header through for CORS<commit_after><|endoftext|>


<commit_before>package xlate

import (
	. "github.com/polydawn/go-xlate/tok"
)

type TokenSource interface {
	Step(fillme *Token) (done bool, err error)
}

type TokenSink interface {
	Step(consume *Token) (done bool, err error)
}

type TokenPump struct {
	TokenSource
	TokenSink
}

func (p TokenPump) Run() {
	// TODO
}

func (p TokenPump) step() {
	// TODO
}
<commit_msg>Implement TokenPump.Run<commit_after>package xlate

import (
	"fmt"

	. "github.com/polydawn/go-xlate/tok"
)

type TokenSource interface {
	Step(fillme *Token) (done bool, err error)
}

type TokenSink interface {
	Step(consume *Token) (done bool, err error)
}

type TokenPump struct {
	TokenSource
	TokenSink
}

func (p TokenPump) Run() error {
	var tok Token
	var srcDone, sinkDone bool
	var err error
	for {
		srcDone, err = p.TokenSource.Step(&tok)
		if err != nil {
			return err
		}
		sinkDone, err = p.TokenSink.Step(&tok)
		if err != nil {
			return err
		}
		if srcDone {
			if sinkDone {
				return nil
			}
			return fmt.Errorf("src at end of item but sink expects more")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"time"
)

func stopContext(c *cli.Context) {
	ctxFileName := os.ExpandEnv(c.GlobalString("ctxfile"))

	slices, errDeserialize := deserialize(ctxFileName)
	if errDeserialize != nil {
		fmt.Printf("%s", errDeserialize)
		return
	}

	if len(slices) == 0 {
		fmt.Println("You must start a context first")
		return
	}

	slice := &slices[len(slices)-1]
	now := time.Now()
	slice.End = &now

	if errSerialize := serialize(ctxFileName, slices); errSerialize != nil {
		fmt.Printf("%s", errSerialize)
		return
	}
}

func switchContext(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Printf("You must provide the id of the context\n")
		return
	}

	contextId := c.Args()[0]
	storage, err := NewStorage(os.ExpandEnv(c.GlobalString("ctxfile")))

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	err = storage.SwitchContext(contextId)

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	fmt.Printf("You're working on %s", contextId)
}

func info(c *cli.Context) {
	storage, err := NewStorage(os.ExpandEnv(c.GlobalString("ctxfile")))

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	context := storage.GetCurrentContext()
	fmt.Print("%s", context.GetTotalDuration())

	// slices, err := deserialize(ctxFileName)
	// if err != nil {
	//     fmt.Printf("%s", err)
	//     return
	// }

	// if len(slices) == 0 {
	//     fmt.Println("You have not started a context")
	//     return
	// }

	// var duration time.Duration

	// for _, slice := range slices {
	//     if slice.IsComplete() {
	//         duration += slice.Duration()
	//     }
	// }

	// lastSlice := slices[len(slices)-1]
	// if !lastSlice.IsComplete() {
	//     now := time.Now()
	//     duration += now.Sub(*lastSlice.Start)
	// }

	// fmt.Println(duration)
}
<commit_msg>call the right print<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"time"
)

func stopContext(c *cli.Context) {
	ctxFileName := os.ExpandEnv(c.GlobalString("ctxfile"))

	slices, errDeserialize := deserialize(ctxFileName)
	if errDeserialize != nil {
		fmt.Printf("%s", errDeserialize)
		return
	}

	if len(slices) == 0 {
		fmt.Println("You must start a context first")
		return
	}

	slice := &slices[len(slices)-1]
	now := time.Now()
	slice.End = &now

	if errSerialize := serialize(ctxFileName, slices); errSerialize != nil {
		fmt.Printf("%s", errSerialize)
		return
	}
}

func switchContext(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Printf("You must provide the id of the context\n")
		return
	}

	contextId := c.Args()[0]
	storage, err := NewStorage(os.ExpandEnv(c.GlobalString("ctxfile")))

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	err = storage.SwitchContext(contextId)

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	fmt.Printf("You're working on %s", contextId)
}

func info(c *cli.Context) {
	storage, err := NewStorage(os.ExpandEnv(c.GlobalString("ctxfile")))

	if err != nil {
		fmt.Printf("%s", err)
		return
	}

	context := storage.GetCurrentContext()
	fmt.Printf("%d", context.GetTotalDuration())

	// slices, err := deserialize(ctxFileName)
	// if err != nil {
	//     fmt.Printf("%s", err)
	//     return
	// }

	// if len(slices) == 0 {
	//     fmt.Println("You have not started a context")
	//     return
	// }

	// var duration time.Duration

	// for _, slice := range slices {
	//     if slice.IsComplete() {
	//         duration += slice.Duration()
	//     }
	// }

	// lastSlice := slices[len(slices)-1]
	// if !lastSlice.IsComplete() {
	//     now := time.Now()
	//     duration += now.Sub(*lastSlice.Start)
	// }

	// fmt.Println(duration)
}
<|endoftext|>


<commit_before>package system

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/b1101/systemgo/unit"
	"github.com/b1101/systemgo/unit/service"

	log "github.com/Sirupsen/logrus"
)

var DEFAULT_PATHS = []string{"/etc/systemd/system/", "/run/systemd/system", "/lib/systemd/system"}

type Daemon struct {
	// Map containing pointers to all currently active units(name -> *Unit)
	active map[string]*Unit

	// Map containing pointers to all successfully loaded units(name -> *Unit)
	loaded map[string]*Unit

	// Map containing pointers to all parsed units, including those failed to load(name -> *Unit)
	parsed map[string]*Unit

	// Paths, where the unit file specifications get searched for
	Paths []string

	// System state
	State State

	// Starting time
	Since time.Time

	// System log
	Log *Log

	jobs []unit.Starter
}

var supported = map[string]bool{
	".service": true,
	".target":  true,
	".mount":   false,
	".socket":  false,
}

// SupportedSuffix returns a bool indicating if suffix represents a unit type,
// which is supported by Systemgo
func SupportedSuffix(suffix string) bool {
	return supported[suffix]
}

// Supported returns a bool indicating if filename represents a unit type,
// which is supported by Systemgo
func Supported(filename string) bool {
	return SupportedSuffix(filepath.Ext(filename))
}

func New() (sys *Daemon) {
	defer func() {
		if debug {
			sys.Log.Logger.Hooks.Add(&errorHook{
				Source: "system",
			})
		}
	}()
	return &Daemon{
		active: make(map[string]*Unit),
		loaded: make(map[string]*Unit),
		parsed: make(map[string]*Unit),

		Since: time.Now(),
		Log:   NewLog(),
		Paths: DEFAULT_PATHS,
	}
}

func (sys *Daemon) SetPaths(paths ...string) {
	sys.Paths = paths
}

// Status returns status of the system
// If error is returned it is going to be an error,
// returned by the call to ioutil.ReadAll(sys.Log)
func (sys *Daemon) Status() (st Status, err error) {
	st = Status{
		State: sys.State,
		Since: sys.Since,
	}

	st.Log, err = ioutil.ReadAll(sys.Log)

	return
}

func (sys *Daemon) Start(names ...string) (err error) {
	var units map[string]*Unit
	if units, err = sys.loadDeps(names); err != nil {
		return
	}

	var ordering []*Unit
	if ordering, err = sys.order(units); err != nil {
		return
	}

	for _, u := range ordering {
		go u.Start()
	}

	//var job *Job
	//if job, err = sys.NewJob(start, names...); err != nil {
	//return
	//}

	//return job.Start()
	//t := NewTarget(sys)
	//th
	return
}

func (sys *Daemon) Stop(name string) (err error) {

	return nil
}

func (sys *Daemon) Restart(name string) (err error) {
	if err = sys.Stop(name); err != nil {
		return
	}
	return sys.Start(name)
}

func (sys *Daemon) Reload(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}

	if reloader, ok := u.Interface.(unit.Reloader); ok {
		return reloader.Reload()
	}

	return ErrNoReload
}

// TODO
func (sys *Daemon) Enable(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}
	u.Log.Println("enable")
	return ErrNotImplemented
}

// TODO
func (sys *Daemon) Disable(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}
	u.Log.Println("disable")
	return ErrNotImplemented
}

// IsEnabled returns enable state of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) IsEnabled(name string) (st unit.Enable, err error) {
	//var u *Unit
	//if u, err = sys.Unit(name); err == nil && sys.Enabled[u] {
	//st = unit.Enabled
	//}
	return unit.Enabled, ErrNotImplemented
}

// IsActive returns activation state of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) IsActive(name string) (st unit.Activation, err error) {
	var u *Unit
	if u, err = sys.Get(name); err == nil {
		st = u.Active()
	}
	return
}

var std = New()

// Get looks up the unit name in the internal hasmap of loaded units and calls
// sys.Load(name) if it can not be found
// If error is returned, it will be error from sys.Load(name)
func (sys *Daemon) Get(name string) (u *Unit, err error) {
	var ok bool
	if u, ok = sys.loaded[name]; !ok {
		u, err = sys.Load(name)
	}
	return
}

// StatusOf returns status of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) StatusOf(name string) (st unit.Status, err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}

	st = unit.Status{
		Load: unit.LoadStatus{
			Path:   u.Path(),
			Loaded: u.Loaded(),
			State:  unit.Enabled,
		},
		Activation: unit.ActivationStatus{
			State: u.Active(),
			Sub:   u.Sub(),
		},
	}

	st.Log, err = ioutil.ReadAll(u.Log)

	return
}

// Load searches for a definition of unit name in configured paths parses it and returns a pointer to Unit
// If a unit name has already been parsed(tried to load) by sys, it will not create a new unit, but return a pointer to that unit instead
func (sys *Daemon) Load(name string) (u *Unit, err error) {
	if !Supported(name) {
		return nil, ErrUnknownType
	}

	var paths []string
	if filepath.IsAbs(name) {
		paths = []string{name}
	} else {
		paths = make([]string, len(sys.Paths))
		for i, path := range sys.Paths {
			paths[i] = filepath.Join(path, name)
		}
	}

	for _, path := range paths {
		var file *os.File
		if file, err = os.Open(path); err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, err
		}
		defer file.Close()

		var parsed bool
		if u, parsed = sys.parsed[name]; !parsed {
			var v unit.Interface
			switch filepath.Ext(path) {
			case ".target":
				v = &Target{Getter: sys}
			case ".service":
				v = &service.Unit{}
			default:
				log.Fatalln("Trying to load an unsupported unit type")
			}

			u = NewUnit(v)
			sys.parsed[name] = u
			sys.Log.Debugf("Created a *Unit wrapping %s and put into internal hashmap")

			if name != path {
				sys.parsed[path] = u
			}

			if debug {
				u.Log.Logger.Hooks.Add(&errorHook{
					Source: name,
				})
			}
		}

		u.path = path

		var info os.FileInfo
		if info, err = file.Stat(); err == nil && info.IsDir() {
			err = ErrIsDir
		}
		if err != nil {
			u.Log.Printf("%s", err)
			return u, err
		}

		if err = u.Interface.Define(file); err != nil {
			if me, ok := err.(unit.MultiError); ok {
				u.Log.Printf("Definition is invalid:")
				for _, errmsg := range me.Errors() {
					u.Log.Printf(errmsg)
				}
			} else {
				u.Log.Printf("Error parsing definition: %s", err)
			}
			u.loaded = unit.Error
			return u, err
		}

		u.loaded = unit.Loaded
		sys.loaded[name] = u
		sys.Log.Debugf("Unit %s loaded and put into internal hashmap", name)
		return u, err
	}

	return nil, ErrNotFound
}

//func (sys Daemon) WriteStatus(output io.Writer, names ...string) (err error) {
//if len(names) == 0 {
//w := tabwriter.Writer
//out += fmt.Sprintln("unit\t\t\t\tload\tactive\tsub\tdescription")
//out += fmt.Sprintln(s.Units)
//}

//func (us units) String() (out string) {
//for _, u := range us {
//out += fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t\n",
//u.Name(), u.Loaded(), u.Active(), u.Sub(), u.Description())
//}
//return
//}

// pathset returns a slice of paths to definitions of supported unit types found in path specified
func pathset(path string) (definitions []string, err error) {
	var file *os.File
	if file, err = os.Open(path); err != nil {
		return nil, err
	}
	defer file.Close()

	var info os.FileInfo
	if info, err = file.Stat(); err != nil {
		return nil, err
	} else if !info.IsDir() {
		return nil, ErrNotDir
	}

	var names []string
	if names, err = file.Readdirnames(0); err != nil {
		return nil, err
	}

	definitions = make([]string, 0, len(names))
	for _, name := range names {
		if Supported(name) {
			definitions = append(definitions, filepath.Clean(path+"/"+name))
		}
	}

	return
}

func (sys *Daemon) loadDeps(names []string) (units map[string]*Unit, err error) {
	units = map[string]*Unit{}
	added := func(name string) (is bool) {
		_, is = units[name]
		return
	}

	var failed bool
	for len(names) > 0 {
		var u *Unit
		name := names[0]

		if !added(name) {
			if u, err = sys.Get(name); err != nil {
				return nil, fmt.Errorf("Error loading dependency: %s", name)
			}
			units[name] = u

			names = append(names, u.Requires()...)

			for _, name := range u.Wants() {
				if !added(name) {
					units[name], _ = sys.Get(name)
				}
			}
		}

		names = names[1:]
	}
	if failed {
		return nil, ErrDepFail
	}

	return
}

type graph struct {
	ordered  map[*Unit]struct{}
	visited  map[*Unit]struct{}
	before   map[*Unit]map[string]*Unit
	ordering []*Unit
}

func (sys *Daemon) order(units map[string]*Unit) (ordering []*Unit, err error) {
	g := &graph{
		map[*Unit]struct{}{},
		map[*Unit]struct{}{},
		map[*Unit]map[string]*Unit{},
		make([]*Unit, 0, len(units)),
	}

	for _, unit := range units {
		g.before[unit] = map[string]*Unit{}
	}

	for name, unit := range units {
		for _, depname := range unit.After() {
			log.Debugln(name, " after ", depname)
			if dep, ok := units[depname]; ok {
				g.before[unit][depname] = dep
			}
		}

		for _, depname := range unit.Before() {
			log.Debugln(name, " before ", depname)
			if dep, ok := units[depname]; ok {
				g.before[dep][name] = unit
			}
		}
	}

	for name, unit := range units {
		if err = g.traverse(unit); err != nil {
			return nil, fmt.Errorf("Dependency cycle determined:\n%s depends on %s", name, err)
		}
	}

	return g.ordering, nil
}

var errBlank = errors.New("")

func (g *graph) traverse(u *Unit) (err error) {
	if _, has := g.ordered[u]; has {
		return nil
	}

	if _, has := g.visited[u]; has {
		return errBlank
	}

	g.visited[u] = struct{}{}

	for name, dep := range g.before[u] {
		if err = g.traverse(dep); err != nil {
			if err == errBlank {
				return fmt.Errorf("%s\n", name)
			}
			return fmt.Errorf("%s\n%s depends on %s", name, name, err)
		}
	}

	delete(g.visited, u)

	if _, has := g.ordered[u]; !has {
		g.ordering = append(g.ordering, u)
		g.ordered[u] = struct{}{}
	}

	return nil
}
<commit_msg>system.Daemon: track unit names in a hashmap<commit_after>package system

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/b1101/systemgo/unit"
	"github.com/b1101/systemgo/unit/service"

	log "github.com/Sirupsen/logrus"
)

var DEFAULT_PATHS = []string{"/etc/systemd/system/", "/run/systemd/system", "/lib/systemd/system"}

type Daemon struct {
	// Map containing pointers to all currently active units(name -> *Unit)
	active map[string]*Unit

	// Map containing pointers to all successfully loaded units(name -> *Unit)
	loaded map[string]*Unit

	// Map containing pointers to all parsed units, including those failed to load(name -> *Unit)
	parsed map[string]*Unit

	// Map containing name of each unit (*Unit -> name)
	names map[*Unit]string

	// Paths, where the unit file specifications get searched for
	Paths []string

	// System state
	State State

	// Starting time
	Since time.Time

	// System log
	Log *Log

	jobs []unit.Starter
}

var supported = map[string]bool{
	".service": true,
	".target":  true,
	".mount":   false,
	".socket":  false,
}

// SupportedSuffix returns a bool indicating if suffix represents a unit type,
// which is supported by Systemgo
func SupportedSuffix(suffix string) bool {
	return supported[suffix]
}

// Supported returns a bool indicating if filename represents a unit type,
// which is supported by Systemgo
func Supported(filename string) bool {
	return SupportedSuffix(filepath.Ext(filename))
}

func New() (sys *Daemon) {
	defer func() {
		if debug {
			sys.Log.Logger.Hooks.Add(&errorHook{
				Source: "system",
			})
		}
	}()
	return &Daemon{
		active: make(map[string]*Unit),
		loaded: make(map[string]*Unit),
		parsed: make(map[string]*Unit),
		names:  make(map[*Unit]string),

		Since: time.Now(),
		Log:   NewLog(),
		Paths: DEFAULT_PATHS,
	}
}

func (sys *Daemon) SetPaths(paths ...string) {
	sys.Paths = paths
}

// Status returns status of the system
// If error is returned it is going to be an error,
// returned by the call to ioutil.ReadAll(sys.Log)
func (sys *Daemon) Status() (st Status, err error) {
	st = Status{
		State: sys.State,
		Since: sys.Since,
	}

	st.Log, err = ioutil.ReadAll(sys.Log)

	return
}

func (sys *Daemon) Start(names ...string) (err error) {
	var units map[string]*Unit
	if units, err = sys.loadDeps(names); err != nil {
		return
	}

	var ordering []*Unit
	if ordering, err = sys.order(units); err != nil {
		return
	}

	for _, u := range ordering {
		sys.active[sys.nameOf(u)] = u
		go u.Start()
	}

	return
}

func (sys *Daemon) Stop(name string) (err error) {
	return nil
}

func (sys *Daemon) Restart(name string) (err error) {
	if err = sys.Stop(name); err != nil {
		return
	}
	return sys.Start(name)
}

func (sys *Daemon) Reload(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}

	if reloader, ok := u.Interface.(unit.Reloader); ok {
		return reloader.Reload()
	}

	return ErrNoReload
}

// TODO
func (sys *Daemon) Enable(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}
	u.Log.Println("enable")
	return ErrNotImplemented
}

// TODO
func (sys *Daemon) Disable(name string) (err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}
	u.Log.Println("disable")
	return ErrNotImplemented
}

// IsEnabled returns enable state of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) IsEnabled(name string) (st unit.Enable, err error) {
	//var u *Unit
	//if u, err = sys.Unit(name); err == nil && sys.Enabled[u] {
	//st = unit.Enabled
	//}
	return unit.Enabled, ErrNotImplemented
}

// IsActive returns activation state of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) IsActive(name string) (st unit.Activation, err error) {
	var u *Unit
	if u, err = sys.Get(name); err == nil {
		st = u.Active()
	}
	return
}

var std = New()

// Get looks up the unit name in the internal hasmap of loaded units and calls
// sys.Load(name) if it can not be found
// If error is returned, it will be error from sys.Load(name)
func (sys *Daemon) Get(name string) (u *Unit, err error) {
	var ok bool
	if u, ok = sys.loaded[name]; !ok {
		u, err = sys.Load(name)
	}
	return
}

// StatusOf returns status of the unit held in-memory under specified name
// If error is returned, it is going to be ErrNotFound
func (sys *Daemon) StatusOf(name string) (st unit.Status, err error) {
	var u *Unit
	if u, err = sys.Get(name); err != nil {
		return
	}

	st = unit.Status{
		Load: unit.LoadStatus{
			Path:   u.Path(),
			Loaded: u.Loaded(),
			State:  unit.Enabled,
		},
		Activation: unit.ActivationStatus{
			State: u.Active(),
			Sub:   u.Sub(),
		},
	}

	st.Log, err = ioutil.ReadAll(u.Log)

	return
}

// Load searches for a definition of unit name in configured paths parses it and returns a pointer to Unit
// If a unit name has already been parsed(tried to load) by sys, it will not create a new unit, but return a pointer to that unit instead
func (sys *Daemon) Load(name string) (u *Unit, err error) {
	if !Supported(name) {
		return nil, ErrUnknownType
	}

	var paths []string
	if filepath.IsAbs(name) {
		paths = []string{name}
	} else {
		paths = make([]string, len(sys.Paths))
		for i, path := range sys.Paths {
			paths[i] = filepath.Join(path, name)
		}
	}

	for _, path := range paths {
		var file *os.File
		if file, err = os.Open(path); err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, err
		}
		defer file.Close()

		var parsed bool
		if u, parsed = sys.parsed[name]; !parsed {
			var v unit.Interface
			switch filepath.Ext(path) {
			case ".target":
				v = &Target{Getter: sys}
			case ".service":
				v = &service.Unit{}
			default:
				log.Fatalln("Trying to load an unsupported unit type")
			}

			u = NewUnit(v)

			sys.names[u] = name
			sys.parsed[name] = u

			sys.Log.Debugf("Created a *Unit wrapping %s and put into internal hashmap")

			if name != path {
				sys.parsed[path] = u
			}

			if debug {
				u.Log.Logger.Hooks.Add(&errorHook{
					Source: name,
				})
			}
		}

		u.path = path

		var info os.FileInfo
		if info, err = file.Stat(); err == nil && info.IsDir() {
			err = ErrIsDir
		}
		if err != nil {
			u.Log.Printf("%s", err)
			return u, err
		}

		if err = u.Interface.Define(file); err != nil {
			if me, ok := err.(unit.MultiError); ok {
				u.Log.Printf("Definition is invalid:")
				for _, errmsg := range me.Errors() {
					u.Log.Printf(errmsg)
				}
			} else {
				u.Log.Printf("Error parsing definition: %s", err)
			}
			u.loaded = unit.Error
			return u, err
		}

		u.loaded = unit.Loaded
		sys.loaded[name] = u
		sys.Log.Debugf("Unit %s loaded and put into internal hashmap", name)
		return u, err
	}

	return nil, ErrNotFound
}

//func (sys Daemon) WriteStatus(output io.Writer, names ...string) (err error) {
//if len(names) == 0 {
//w := tabwriter.Writer
//out += fmt.Sprintln("unit\t\t\t\tload\tactive\tsub\tdescription")
//out += fmt.Sprintln(s.Units)
//}

//func (us units) String() (out string) {
//for _, u := range us {
//out += fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t\n",
//u.Name(), u.Loaded(), u.Active(), u.Sub(), u.Description())
//}
//return
//}

// pathset returns a slice of paths to definitions of supported unit types found in path specified
func pathset(path string) (definitions []string, err error) {
	var file *os.File
	if file, err = os.Open(path); err != nil {
		return nil, err
	}
	defer file.Close()

	var info os.FileInfo
	if info, err = file.Stat(); err != nil {
		return nil, err
	} else if !info.IsDir() {
		return nil, ErrNotDir
	}

	var names []string
	if names, err = file.Readdirnames(0); err != nil {
		return nil, err
	}

	definitions = make([]string, 0, len(names))
	for _, name := range names {
		if Supported(name) {
			definitions = append(definitions, filepath.Clean(path+"/"+name))
		}
	}

	return
}

func (sys *Daemon) loadDeps(names []string) (units map[string]*Unit, err error) {
	units = map[string]*Unit{}
	added := func(name string) (is bool) {
		_, is = units[name]
		return
	}

	var failed bool
	for len(names) > 0 {
		var u *Unit
		name := names[0]

		if !added(name) {
			if u, err = sys.Get(name); err != nil {
				return nil, fmt.Errorf("Error loading dependency: %s", name)
			}
			units[name] = u

			names = append(names, u.Requires()...)

			for _, name := range u.Wants() {
				if !added(name) {
					units[name], _ = sys.Get(name)
				}
			}
		}

		names = names[1:]
	}
	if failed {
		return nil, ErrDepFail
	}

	return
}

type graph struct {
	ordered  map[*Unit]struct{}
	visited  map[*Unit]struct{}
	before   map[*Unit]map[string]*Unit
	ordering []*Unit
}

func (sys *Daemon) order(units map[string]*Unit) (ordering []*Unit, err error) {
	g := &graph{
		map[*Unit]struct{}{},
		map[*Unit]struct{}{},
		map[*Unit]map[string]*Unit{},
		make([]*Unit, 0, len(units)),
	}

	for _, unit := range units {
		g.before[unit] = map[string]*Unit{}
	}

	for name, unit := range units {
		for _, depname := range unit.After() {
			log.Debugln(name, " after ", depname)
			if dep, ok := units[depname]; ok {
				g.before[unit][depname] = dep
			}
		}

		for _, depname := range unit.Before() {
			log.Debugln(name, " before ", depname)
			if dep, ok := units[depname]; ok {
				g.before[dep][name] = unit
			}
		}
	}

	for name, unit := range units {
		if err = g.traverse(unit); err != nil {
			return nil, fmt.Errorf("Dependency cycle determined:\n%s depends on %s", name, err)
		}
	}

	return g.ordering, nil
}

var errBlank = errors.New("")

func (g *graph) traverse(u *Unit) (err error) {
	if _, has := g.ordered[u]; has {
		return nil
	}

	if _, has := g.visited[u]; has {
		return errBlank
	}

	g.visited[u] = struct{}{}

	for name, dep := range g.before[u] {
		if err = g.traverse(dep); err != nil {
			if err == errBlank {
				return fmt.Errorf("%s\n", name)
			}
			return fmt.Errorf("%s\n%s depends on %s", name, name, err)
		}
	}

	delete(g.visited, u)

	if _, has := g.ordered[u]; !has {
		g.ordering = append(g.ordering, u)
		g.ordered[u] = struct{}{}
	}

	return nil
}

func (sys *Daemon) nameOf(u *Unit) (name string) {
	if name, ok := sys.names[u]; ok {
		return name
	}
	panic("Unnamed unit")
}
<|endoftext|>