<commit_before>package data_types

type Capabilities struct {
  AddressFamily      []AddressFamily    `yang:"nonconfig" json:"address-family"`
  ForwardingActions  []ForwardingAction `yang:"nonconfig" json:"forwarding-actions"`
  RateLimit          *bool              `yang:"nonconfig" json:"rate-limit"`
  TransportProtocols UInt8List          `yang:"nonconfig" json:"transport-protocols"`

  IPv4               *Capabilities_IPv4 `yang:"nonconfig" json:"ipv4"`
  IPv6               *Capabilities_IPv6 `yang:"nonconfig" json:"ipv6"`
  TCP                *Capabilities_TCP  `yang:"nonconfig" json:"tcp"`
  UDP                *Capabilities_UDP  `yang:"nonconfig" json:"udp"`
  ICMP               *Capabilities_ICMP `yang:"nonconfig" json:"icmp"`

  VendorMappingEnabled *bool `yang:"nonconfig" json:"vendor_mapping_enabled"`
}

type Capabilities_IPv4 struct {
  DSCP              *bool `yang:"nonconfig" json:"dscp"`
  ECN               *bool `yang:"nonconfig" json:"ecn"`
  Length            *bool `yang:"nonconfig" json:"length"`
  TTL               *bool `yang:"nonconfig" json:"ttl"`
  Protocol          *bool `yang:"nonconfig" json:"protocol"`
  IHL               *bool `yang:"nonconfig" json:"ihl"`
  Flags             *bool `yang:"nonconfig" json:"flags"`
  Offset            *bool `yang:"nonconfig" json:"offset"`
  Identification    *bool `yang:"nonconfig" json:"identification"`
  SourcePrefix      *bool `yang:"nonconfig" json:"source-prefix"`
  DestinationPrefix *bool `yang:"nonconfig" json:"destination-prefix"`
  Fragment          *bool `yang:"nonconfig" json:"fragment"`
}

type Capabilities_IPv6 struct {
  DSCP              *bool `yang:"nonconfig" json:"dscp"`
  ECN               *bool `yang:"nonconfig" json:"ecn"`
  FlowLabel         *bool `yang:"nonconfig" json:"flow-label"`
  Length            *bool `yang:"nonconfig" json:"length"`
  Protocol          *bool `yang:"nonconfig" json:"protocol"`
  HopLimit          *bool `yang:"nonconfig" json:"hoplimit"`
  Identification    *bool `yang:"nonconfig" json:"identification"`
  SourcePrefix      *bool `yang:"nonconfig" json:"source-prefix"`
  DestinationPrefix *bool `yang:"nonconfig" json:"destination-prefix"`
  Fragment          *bool `yang:"nonconfig" json:"fragment"`
}

type Capabilities_TCP struct {
  SequenceNumber        *bool `yang:"nonconfig" json:"sequence-number"`
  AcknowledgementNumber *bool `yang:"nonconfig" json:"acknowledgement-number"`
  DataOffset            *bool `yang:"nonconfig" json:"data-offset"`
  Reserved              *bool `yang:"nonconfig" json:"reserved"`
  Flags                 *bool `yang:"nonconfig" json:"flags"`
  FlagsBitmask          *bool `yang:"nonconfig" json:"flags-bitmask"`
  WindowSize            *bool `yang:"nonconfig" json:"window-size"`
  UrgentPointer         *bool `yang:"nonconfig" json:"urgent-pointer"`
  Options               *bool `yang:"nonconfig" json:"options"`
  SourcePort            *bool `yang:"nonconfig" json:"source-port"`
  DestinationPort       *bool `yang:"nonconfig" json:"destination-port"`
  PortRange             *bool `yang:"nonconfig" json:"port-range"`
}

type Capabilities_UDP struct {
  Length          *bool `yang:"nonconfig" json:"length"`
  SourcePort      *bool `yang:"nonconfig" json:"source-port"`
  DestinationPort *bool `yang:"nonconfig" json:"destination-port"`
  PortRange       *bool `yang:"nonconfig" json:"port-range"`
}

type Capabilities_ICMP struct {
  Type         *bool `yang:"nonconfig" json:"type"`
  Code         *bool `yang:"nonconfig" json:"code"`
  RestOfHeader *bool `yang:"nonconfig" json:"rest-of-header"`
}
<commit_msg>In capabilities should be replaced with ietf-dots-mapping:vendor-mapping-enabled issue #324<commit_after>package data_types

type Capabilities struct {
  AddressFamily      []AddressFamily    `yang:"nonconfig" json:"address-family"`
  ForwardingActions  []ForwardingAction `yang:"nonconfig" json:"forwarding-actions"`
  RateLimit          *bool              `yang:"nonconfig" json:"rate-limit"`
  TransportProtocols UInt8List          `yang:"nonconfig" json:"transport-protocols"`

  IPv4               *Capabilities_IPv4 `yang:"nonconfig" json:"ipv4"`
  IPv6               *Capabilities_IPv6 `yang:"nonconfig" json:"ipv6"`
  TCP                *Capabilities_TCP  `yang:"nonconfig" json:"tcp"`
  UDP                *Capabilities_UDP  `yang:"nonconfig" json:"udp"`
  ICMP               *Capabilities_ICMP `yang:"nonconfig" json:"icmp"`

  VendorMappingEnabled *bool `yang:"nonconfig" json:"ietf-dots-mapping:vendor-mapping-enabled"`
}

type Capabilities_IPv4 struct {
  DSCP              *bool `yang:"nonconfig" json:"dscp"`
  ECN               *bool `yang:"nonconfig" json:"ecn"`
  Length            *bool `yang:"nonconfig" json:"length"`
  TTL               *bool `yang:"nonconfig" json:"ttl"`
  Protocol          *bool `yang:"nonconfig" json:"protocol"`
  IHL               *bool `yang:"nonconfig" json:"ihl"`
  Flags             *bool `yang:"nonconfig" json:"flags"`
  Offset            *bool `yang:"nonconfig" json:"offset"`
  Identification    *bool `yang:"nonconfig" json:"identification"`
  SourcePrefix      *bool `yang:"nonconfig" json:"source-prefix"`
  DestinationPrefix *bool `yang:"nonconfig" json:"destination-prefix"`
  Fragment          *bool `yang:"nonconfig" json:"fragment"`
}

type Capabilities_IPv6 struct {
  DSCP              *bool `yang:"nonconfig" json:"dscp"`
  ECN               *bool `yang:"nonconfig" json:"ecn"`
  FlowLabel         *bool `yang:"nonconfig" json:"flow-label"`
  Length            *bool `yang:"nonconfig" json:"length"`
  Protocol          *bool `yang:"nonconfig" json:"protocol"`
  HopLimit          *bool `yang:"nonconfig" json:"hoplimit"`
  Identification    *bool `yang:"nonconfig" json:"identification"`
  SourcePrefix      *bool `yang:"nonconfig" json:"source-prefix"`
  DestinationPrefix *bool `yang:"nonconfig" json:"destination-prefix"`
  Fragment          *bool `yang:"nonconfig" json:"fragment"`
}

type Capabilities_TCP struct {
  SequenceNumber        *bool `yang:"nonconfig" json:"sequence-number"`
  AcknowledgementNumber *bool `yang:"nonconfig" json:"acknowledgement-number"`
  DataOffset            *bool `yang:"nonconfig" json:"data-offset"`
  Reserved              *bool `yang:"nonconfig" json:"reserved"`
  Flags                 *bool `yang:"nonconfig" json:"flags"`
  FlagsBitmask          *bool `yang:"nonconfig" json:"flags-bitmask"`
  WindowSize            *bool `yang:"nonconfig" json:"window-size"`
  UrgentPointer         *bool `yang:"nonconfig" json:"urgent-pointer"`
  Options               *bool `yang:"nonconfig" json:"options"`
  SourcePort            *bool `yang:"nonconfig" json:"source-port"`
  DestinationPort       *bool `yang:"nonconfig" json:"destination-port"`
  PortRange             *bool `yang:"nonconfig" json:"port-range"`
}

type Capabilities_UDP struct {
  Length          *bool `yang:"nonconfig" json:"length"`
  SourcePort      *bool `yang:"nonconfig" json:"source-port"`
  DestinationPort *bool `yang:"nonconfig" json:"destination-port"`
  PortRange       *bool `yang:"nonconfig" json:"port-range"`
}

type Capabilities_ICMP struct {
  Type         *bool `yang:"nonconfig" json:"type"`
  Code         *bool `yang:"nonconfig" json:"code"`
  RestOfHeader *bool `yang:"nonconfig" json:"rest-of-header"`
}
<|endoftext|>


<commit_before>// fixity_reader periodically queries Fluctus for GenericFiles
// that haven't had a fixity check in X days. The number of
// days is specified in the config file. It then queues those
// items for fixity check in nsqd.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/APTrust/bagman/workers"
	"net/http"
	"os"
	"strings"
	"time"
)

// Queue delete requests in batches of 50.
// Wait X milliseconds between batches.
const (
	batchSize        = 500
	waitMilliseconds = 1000
)

var workReader *bagman.WorkReader

func main() {
	var err error = nil
	workReader, err = workers.InitializeReader()
	workReader.MessageLog.Info("fixity_reader started")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Initialization failed for fixity_reader: %v", err)
		os.Exit(1)
	}
	run()
}

func run() {
	url := fmt.Sprintf("%s/mput?topic=%s", workReader.Config.NsqdHttpAddress,
		workReader.Config.FixityWorker.NsqTopic)
	workReader.MessageLog.Info("Sending files needing fixity check to %s", url)

	daysAgo := time.Duration(workReader.Config.MaxDaysSinceFixityCheck * -24) * time.Hour
	sinceWhen := time.Now().UTC().Add(daysAgo)
	genericFiles, err := workReader.FluctusClient.GetFilesNotCheckedSince(sinceWhen)

	if err != nil {
		workReader.MessageLog.Fatalf("Error getting items items needing fixity check: %v", err)
	}

	workReader.MessageLog.Info("Found %d items needing fixity check", len(genericFiles))

	// Create ProcessedItem records for these? Or do this in Rails?

	start := 0
	end := bagman.Min(len(genericFiles), batchSize)
	for start <= end {
		batch := genericFiles[start:end]
		workReader.MessageLog.Info("Queuing batch of %d items", len(batch))
		enqueue(url, batch)
		start = end + 1
		if start < len(genericFiles) {
			end = bagman.Min(len(genericFiles), start+batchSize)
		}
		time.Sleep(time.Millisecond * waitMilliseconds)
	}
}

// enqueue adds a batch of items to the nsqd work queue
func enqueue(url string, genericFiles []*bagman.GenericFile) {
	jsonData := make([]string, len(genericFiles))
	for i, genericFile := range genericFiles {
		fixityResult := bagman.NewFixityResult(genericFile)
		json, err := json.Marshal(fixityResult)
		if err != nil {
			workReader.MessageLog.Error("Error marshalling FixityResult to JSON: %s", err.Error())
		} else {
			jsonData[i] = string(json)
			workReader.MessageLog.Info("Put %s into fixity_check queue (%s)",
				genericFile.Identifier, genericFile.URI)
		}
	}
	batch := strings.Join(jsonData, "\n")
	resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(batch)))
	if err != nil {
		workReader.MessageLog.Error("nsqd returned an error: %s", err.Error())
	}
	if resp == nil {
		msg := "No response from nsqd. Is it running? fixity_reader is quitting."
		workReader.MessageLog.Error(msg)
		fmt.Println(msg)
		os.Exit(1)
	} else if resp.StatusCode != 200 {
		workReader.MessageLog.Error("nsqd returned status code %d on last mput", resp.StatusCode)
	}
}
<commit_msg>Deleted fixity reader<commit_after><|endoftext|>


<commit_before>package scalecli

import (
    "gopkg.in/resty.v0"
    "fmt"
    "encoding/json"
)

type StrikeIngestFile struct {
    FileNameRegex   string `json:"filename_regex"`
    DataTypes       []string `json:"data_types,omitempty"`
    WorkspacePath   string `json:"workspace_path"`
    WorkspaceName   string `json:"workspace_name"`
}

type StrikeConfiguration struct {
    Version         string `json:"version,omitempty"`
    Mount           string `json:"mount"`
    TransferSuffix  string `json:"transfer_suffix"`
    FilesToIngest   []StrikeIngestFile `json:"files_to_ingest"`
}

type StrikeData struct {
    Name            string `json:"name"`
    Title           string `json:"title,omitempty"`
    Description     string `json:"description,omitempty"`
    Configuration   StrikeConfiguration `json:"configuration"`
}

func CreateStrikeProcess(base_url string, strike_data StrikeData) (strike_id int, err error) {
    json_data, err := json.Marshal(strike_data)
    if err != nil {
        return
    }
    resp, err := resty.R().SetHeaders(map[string]string{
            "Accept":"application/json",
            "Content-type":"application/json",
        }).SetBody(json_data).Post(base_url + "/strikes/")
    if resp == nil && err != nil {
        return
    } else if resp == nil {
        err = fmt.Errorf("Unknown error")
        return
    } else if resp.StatusCode() != 200 {
        err = fmt.Errorf(resp.String())
        return
    }
    var resp_data map[string]interface{}
    err = json.Unmarshal([]byte(resp.Body()), &resp_data)
    if err != nil {
        return
    }
    tmp, ok := resp_data["id"].(float64)
    if ok {
        strike_id = int(tmp)
    } else {
        err = fmt.Errorf("Unknown response %s", resp.Body())
    }
    return
}<commit_msg>Update to Scale CLI for Strike schema change to 2.0<commit_after>package scalecli

import (
    "gopkg.in/resty.v0"
    "fmt"
    "encoding/json"
)

type StrikeIngestFile struct {
    FileNameRegex   string `json:"filename_regex"`
    DataTypes       []string `json:"data_types,omitempty"`
    NewFilePath     string `json:"new_file_path,omitempty"`
    NewWorkspace    string `json:"new_workspace,omitempty"`
}

type StrikeMonitor struct {
    Type            string `json:"type"`
    TransferSuffix  string `json:"transfer_suffix,omitempty"`
    SqsName         string `json:"sqs_name,omitempty"`
}

type StrikeConfiguration struct {
    Version         string `json:"version,omitempty"`
    Workspace       string `json:"workspace"`
    Monitor         StrikeMonitor `json:"monitor"`
    FilesToIngest   []StrikeIngestFile `json:"files_to_ingest"`
}

type StrikeData struct {
    Name            string `json:"name"`
    Title           string `json:"title,omitempty"`
    Description     string `json:"description,omitempty"`
    Configuration   StrikeConfiguration `json:"configuration"`
}

func CreateStrikeProcess(base_url string, strike_data StrikeData) (strike_id int, err error) {
    json_data, err := json.Marshal(strike_data)
    if err != nil {
        return
    }
    resp, err := resty.R().SetHeaders(map[string]string{
            "Accept":"application/json",
            "Content-type":"application/json",
        }).SetBody(json_data).Post(base_url + "/strikes/")
    if resp == nil && err != nil {
        return
    } else if resp == nil {
        err = fmt.Errorf("Unknown error")
        return
    } else if resp.StatusCode() != 200 {
        err = fmt.Errorf(resp.String())
        return
    }
    var resp_data map[string]interface{}
    err = json.Unmarshal([]byte(resp.Body()), &resp_data)
    if err != nil {
        return
    }
    tmp, ok := resp_data["id"].(float64)
    if ok {
        strike_id = int(tmp)
    } else {
        err = fmt.Errorf("Unknown response %s", resp.Body())
    }
    return
}<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"syscall"
	"time"

	c "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	"github.com/flynn/flynn/pkg/exec"
	"github.com/flynn/flynn/pkg/random"
	"github.com/flynn/flynn/pkg/schedutil"
)

type HostSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&HostSuite{})

func (s *HostSuite) TestAttachNonExistentJob(t *c.C) {
	cluster := s.clusterClient(t)
	hosts, err := cluster.ListHosts()
	t.Assert(err, c.IsNil)

	h := s.hostClient(t, hosts[0].ID)

	// Attaching to a non-existent job should error
	_, err = h.Attach(&host.AttachReq{JobID: "none", Flags: host.AttachFlagLogs}, false)
	t.Assert(err, c.NotNil)
}

func (s *HostSuite) TestAttachFinishedInteractiveJob(t *c.C) {
	cluster := s.clusterClient(t)

	// run a quick interactive job
	cmd := exec.CommandUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), "/bin/true")
	cmd.TTY = true
	runErr := make(chan error)
	go func() {
		runErr <- cmd.Run()
	}()
	select {
	case err := <-runErr:
		t.Assert(err, c.IsNil)
	case <-time.After(5 * time.Second):
		t.Fatal("timed out waiting for interactive job")
	}

	h, err := cluster.DialHost(cmd.HostID)
	t.Assert(err, c.IsNil)

	// Getting the logs for the job should fail, as it has none because it was
	// interactive
	attachErr := make(chan error)
	go func() {
		_, err = h.Attach(&host.AttachReq{JobID: cmd.Job.ID, Flags: host.AttachFlagLogs}, false)
		attachErr <- err
	}()
	select {
	case err := <-attachErr:
		t.Assert(err, c.NotNil)
	case <-time.After(time.Second):
		t.Error("timed out waiting for attach")
	}
}

func (s *HostSuite) TestExecCrashingJob(t *c.C) {
	cluster := s.clusterClient(t)

	for _, attach := range []bool{true, false} {
		t.Logf("attach = %v", attach)
		cmd := exec.CommandUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), "sh", "-c", "exit 1")
		if attach {
			cmd.Stdout = ioutil.Discard
			cmd.Stderr = ioutil.Discard
		}
		t.Assert(cmd.Run(), c.DeepEquals, exec.ExitError(1))
	}
}

/*
	Make an 'ish' application on the given host, returning it when
	it has registered readiness with discoverd.

	User will want to defer cmd.Kill() to clean up.
*/
func makeIshApp(cluster *cluster.Client, h cluster.Host, dc *discoverd.Client, extraConfig host.ContainerConfig) (*exec.Cmd, *discoverd.Instance, error) {
	// pick a unique string to use as service name so this works with concurrent tests.
	serviceName := "ish-service-" + random.String(6)

	// run a job that accepts tcp connections and performs tasks we ask of it in its container
	cmd := exec.JobUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), &host.Job{
		Config: host.ContainerConfig{
			Cmd:   []string{"/bin/ish"},
			Ports: []host.Port{{Proto: "tcp"}},
			Env: map[string]string{
				"NAME": serviceName,
			},
		}.Merge(extraConfig),
	})
	cmd.HostID = h.ID()
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}

	// wait for the job to heartbeat and return its address
	services, err := dc.Instances(serviceName, time.Second*100)
	if err != nil {
		cmd.Kill()
		return nil, nil, err
	}
	if len(services) != 1 {
		cmd.Kill()
		return nil, nil, fmt.Errorf("test setup: expected exactly one service instance, got %d", len(services))
	}

	return cmd, services[0], nil
}

func runIshCommand(service *discoverd.Instance, cmd string) (string, error) {
	resp, err := http.Post(
		fmt.Sprintf("http://%s/ish", service.Addr),
		"text/plain",
		strings.NewReader(cmd),
	)
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func (s *HostSuite) TestNetworkedPersistentJob(t *c.C) {
	// this isn't much more impressive than what's already running by the time we've got a cluster engaged
	// but the idea is to use this basic design to enable testing a series manipulations on a single container.

	cluster := s.clusterClient(t)

	// run a job that accepts tcp connections and performs tasks we ask of it in its container
	cmd, service, err := makeIshApp(cluster, s.anyHostClient(t), s.discoverdClient(t), host.ContainerConfig{})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()

	// test that we can interact with that job
	resp, err := runIshCommand(service, "echo echocococo")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "echocococo\n")
}

func (s *HostSuite) TestVolumeCreation(t *c.C) {
	h := s.anyHostClient(t)

	vol, err := h.CreateVolume("default")
	t.Assert(err, c.IsNil)
	t.Assert(vol.ID, c.Not(c.Equals), "")
	t.Assert(h.DestroyVolume(vol.ID), c.IsNil)
}

func (s *HostSuite) TestVolumeCreationFailsForNonexistentProvider(t *c.C) {
	h := s.anyHostClient(t)

	_, err := h.CreateVolume("non-existent")
	t.Assert(err, c.NotNil)
}

func (s *HostSuite) TestVolumePersistence(t *c.C) {
	t.Skip("test intermittently fails due to host bind mount leaks, see https://github.com/flynn/flynn/issues/1125")

	// most of the volume tests (snapshotting, quotas, etc) are unit tests under their own package.
	// these tests exist to cover the last mile where volumes are bind-mounted into containers.

	cluster := s.clusterClient(t)
	h := s.anyHostClient(t)

	// create a volume!
	vol, err := h.CreateVolume("default")
	t.Assert(err, c.IsNil)
	defer func() {
		t.Assert(h.DestroyVolume(vol.ID), c.IsNil)
	}()

	// create first job
	cmd, service, err := makeIshApp(cluster, h, s.discoverdClient(t), host.ContainerConfig{
		Volumes: []host.VolumeBinding{{
			Target:    "/vol",
			VolumeID:  vol.ID,
			Writeable: true,
		}},
	})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()
	// add data to the volume
	resp, err := runIshCommand(service, "echo 'testcontent' > /vol/alpha ; echo $?")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "0\n")

	// start another one that mounts the same volume
	cmd, service, err = makeIshApp(cluster, h, s.discoverdClient(t), host.ContainerConfig{
		Volumes: []host.VolumeBinding{{
			Target:    "/vol",
			VolumeID:  vol.ID,
			Writeable: false,
		}},
	})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()
	// read data back from the volume
	resp, err = runIshCommand(service, "cat /vol/alpha")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "testcontent\n")
}

func (s *HostSuite) TestSignalJob(t *c.C) {
	cluster := s.clusterClient(t)

	// pick a host to run the job on
	hosts, err := cluster.ListHosts()
	t.Assert(err, c.IsNil)
	hostID := schedutil.PickHost(hosts).ID

	// start a signal-service job
	cmd := exec.JobUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), &host.Job{
		Config: host.ContainerConfig{Cmd: []string{"/bin/signal"}},
	})
	cmd.HostID = hostID
	var out bytes.Buffer
	cmd.Stdout = &out
	t.Assert(cmd.Start(), c.IsNil)
	_, err = s.discoverdClient(t).Instances("signal-service", 10*time.Second)
	t.Assert(err, c.IsNil)

	// send the job a signal
	client, err := cluster.DialHost(hostID)
	t.Assert(err, c.IsNil)
	t.Assert(client.SignalJob(cmd.Job.ID, int(syscall.SIGTERM)), c.IsNil)

	// wait for the job to exit
	done := make(chan error)
	go func() {
		done <- cmd.Wait()
	}()
	select {
	case err := <-done:
		t.Assert(err, c.IsNil)
	case <-time.After(12 * time.Second):
		t.Fatal("timed out waiting for job to stop")
	}

	// check the output
	t.Assert(out.String(), c.Equals, "got signal: terminated")
}

func (s *HostSuite) TestResourceLimits(t *c.C) {
	cmd := exec.JobUsingCluster(
		s.clusterClient(t),
		exec.DockerImage(imageURIs["test-apps"]),
		&host.Job{
			Config:    host.ContainerConfig{Cmd: []string{"sh", "-c", resourceCmd}},
			Resources: testResources(),
		},
	)
	var out bytes.Buffer
	cmd.Stdout = &out

	runErr := make(chan error)
	go func() {
		runErr <- cmd.Run()
	}()
	select {
	case err := <-runErr:
		t.Assert(err, c.IsNil)
	case <-time.After(30 * time.Second):
		t.Fatal("timed out waiting for resource limits job")
	}

	assertResourceLimits(t, out.String())
}
<commit_msg>test: Increase timeout for host attach job<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"syscall"
	"time"

	c "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	"github.com/flynn/flynn/pkg/exec"
	"github.com/flynn/flynn/pkg/random"
	"github.com/flynn/flynn/pkg/schedutil"
)

type HostSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&HostSuite{})

func (s *HostSuite) TestAttachNonExistentJob(t *c.C) {
	cluster := s.clusterClient(t)
	hosts, err := cluster.ListHosts()
	t.Assert(err, c.IsNil)

	h := s.hostClient(t, hosts[0].ID)

	// Attaching to a non-existent job should error
	_, err = h.Attach(&host.AttachReq{JobID: "none", Flags: host.AttachFlagLogs}, false)
	t.Assert(err, c.NotNil)
}

func (s *HostSuite) TestAttachFinishedInteractiveJob(t *c.C) {
	cluster := s.clusterClient(t)

	// run a quick interactive job
	cmd := exec.CommandUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), "/bin/true")
	cmd.TTY = true
	runErr := make(chan error)
	go func() {
		runErr <- cmd.Run()
	}()
	select {
	case err := <-runErr:
		t.Assert(err, c.IsNil)
	case <-time.After(30 * time.Second):
		t.Fatal("timed out waiting for interactive job")
	}

	h, err := cluster.DialHost(cmd.HostID)
	t.Assert(err, c.IsNil)

	// Getting the logs for the job should fail, as it has none because it was
	// interactive
	attachErr := make(chan error)
	go func() {
		_, err = h.Attach(&host.AttachReq{JobID: cmd.Job.ID, Flags: host.AttachFlagLogs}, false)
		attachErr <- err
	}()
	select {
	case err := <-attachErr:
		t.Assert(err, c.NotNil)
	case <-time.After(time.Second):
		t.Error("timed out waiting for attach")
	}
}

func (s *HostSuite) TestExecCrashingJob(t *c.C) {
	cluster := s.clusterClient(t)

	for _, attach := range []bool{true, false} {
		t.Logf("attach = %v", attach)
		cmd := exec.CommandUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), "sh", "-c", "exit 1")
		if attach {
			cmd.Stdout = ioutil.Discard
			cmd.Stderr = ioutil.Discard
		}
		t.Assert(cmd.Run(), c.DeepEquals, exec.ExitError(1))
	}
}

/*
	Make an 'ish' application on the given host, returning it when
	it has registered readiness with discoverd.

	User will want to defer cmd.Kill() to clean up.
*/
func makeIshApp(cluster *cluster.Client, h cluster.Host, dc *discoverd.Client, extraConfig host.ContainerConfig) (*exec.Cmd, *discoverd.Instance, error) {
	// pick a unique string to use as service name so this works with concurrent tests.
	serviceName := "ish-service-" + random.String(6)

	// run a job that accepts tcp connections and performs tasks we ask of it in its container
	cmd := exec.JobUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), &host.Job{
		Config: host.ContainerConfig{
			Cmd:   []string{"/bin/ish"},
			Ports: []host.Port{{Proto: "tcp"}},
			Env: map[string]string{
				"NAME": serviceName,
			},
		}.Merge(extraConfig),
	})
	cmd.HostID = h.ID()
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}

	// wait for the job to heartbeat and return its address
	services, err := dc.Instances(serviceName, time.Second*100)
	if err != nil {
		cmd.Kill()
		return nil, nil, err
	}
	if len(services) != 1 {
		cmd.Kill()
		return nil, nil, fmt.Errorf("test setup: expected exactly one service instance, got %d", len(services))
	}

	return cmd, services[0], nil
}

func runIshCommand(service *discoverd.Instance, cmd string) (string, error) {
	resp, err := http.Post(
		fmt.Sprintf("http://%s/ish", service.Addr),
		"text/plain",
		strings.NewReader(cmd),
	)
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func (s *HostSuite) TestNetworkedPersistentJob(t *c.C) {
	// this isn't much more impressive than what's already running by the time we've got a cluster engaged
	// but the idea is to use this basic design to enable testing a series manipulations on a single container.

	cluster := s.clusterClient(t)

	// run a job that accepts tcp connections and performs tasks we ask of it in its container
	cmd, service, err := makeIshApp(cluster, s.anyHostClient(t), s.discoverdClient(t), host.ContainerConfig{})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()

	// test that we can interact with that job
	resp, err := runIshCommand(service, "echo echocococo")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "echocococo\n")
}

func (s *HostSuite) TestVolumeCreation(t *c.C) {
	h := s.anyHostClient(t)

	vol, err := h.CreateVolume("default")
	t.Assert(err, c.IsNil)
	t.Assert(vol.ID, c.Not(c.Equals), "")
	t.Assert(h.DestroyVolume(vol.ID), c.IsNil)
}

func (s *HostSuite) TestVolumeCreationFailsForNonexistentProvider(t *c.C) {
	h := s.anyHostClient(t)

	_, err := h.CreateVolume("non-existent")
	t.Assert(err, c.NotNil)
}

func (s *HostSuite) TestVolumePersistence(t *c.C) {
	t.Skip("test intermittently fails due to host bind mount leaks, see https://github.com/flynn/flynn/issues/1125")

	// most of the volume tests (snapshotting, quotas, etc) are unit tests under their own package.
	// these tests exist to cover the last mile where volumes are bind-mounted into containers.

	cluster := s.clusterClient(t)
	h := s.anyHostClient(t)

	// create a volume!
	vol, err := h.CreateVolume("default")
	t.Assert(err, c.IsNil)
	defer func() {
		t.Assert(h.DestroyVolume(vol.ID), c.IsNil)
	}()

	// create first job
	cmd, service, err := makeIshApp(cluster, h, s.discoverdClient(t), host.ContainerConfig{
		Volumes: []host.VolumeBinding{{
			Target:    "/vol",
			VolumeID:  vol.ID,
			Writeable: true,
		}},
	})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()
	// add data to the volume
	resp, err := runIshCommand(service, "echo 'testcontent' > /vol/alpha ; echo $?")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "0\n")

	// start another one that mounts the same volume
	cmd, service, err = makeIshApp(cluster, h, s.discoverdClient(t), host.ContainerConfig{
		Volumes: []host.VolumeBinding{{
			Target:    "/vol",
			VolumeID:  vol.ID,
			Writeable: false,
		}},
	})
	t.Assert(err, c.IsNil)
	defer cmd.Kill()
	// read data back from the volume
	resp, err = runIshCommand(service, "cat /vol/alpha")
	t.Assert(err, c.IsNil)
	t.Assert(resp, c.Equals, "testcontent\n")
}

func (s *HostSuite) TestSignalJob(t *c.C) {
	cluster := s.clusterClient(t)

	// pick a host to run the job on
	hosts, err := cluster.ListHosts()
	t.Assert(err, c.IsNil)
	hostID := schedutil.PickHost(hosts).ID

	// start a signal-service job
	cmd := exec.JobUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), &host.Job{
		Config: host.ContainerConfig{Cmd: []string{"/bin/signal"}},
	})
	cmd.HostID = hostID
	var out bytes.Buffer
	cmd.Stdout = &out
	t.Assert(cmd.Start(), c.IsNil)
	_, err = s.discoverdClient(t).Instances("signal-service", 10*time.Second)
	t.Assert(err, c.IsNil)

	// send the job a signal
	client, err := cluster.DialHost(hostID)
	t.Assert(err, c.IsNil)
	t.Assert(client.SignalJob(cmd.Job.ID, int(syscall.SIGTERM)), c.IsNil)

	// wait for the job to exit
	done := make(chan error)
	go func() {
		done <- cmd.Wait()
	}()
	select {
	case err := <-done:
		t.Assert(err, c.IsNil)
	case <-time.After(12 * time.Second):
		t.Fatal("timed out waiting for job to stop")
	}

	// check the output
	t.Assert(out.String(), c.Equals, "got signal: terminated")
}

func (s *HostSuite) TestResourceLimits(t *c.C) {
	cmd := exec.JobUsingCluster(
		s.clusterClient(t),
		exec.DockerImage(imageURIs["test-apps"]),
		&host.Job{
			Config:    host.ContainerConfig{Cmd: []string{"sh", "-c", resourceCmd}},
			Resources: testResources(),
		},
	)
	var out bytes.Buffer
	cmd.Stdout = &out

	runErr := make(chan error)
	go func() {
		runErr <- cmd.Run()
	}()
	select {
	case err := <-runErr:
		t.Assert(err, c.IsNil)
	case <-time.After(30 * time.Second):
		t.Fatal("timed out waiting for resource limits job")
	}

	assertResourceLimits(t, out.String())
}
<|endoftext|>


<commit_before>package schema

import (
	"encoding/binary"
	"hash/fnv"

	"github.com/cespare/xxhash"
	jump "github.com/dgryski/go-jump"
)

type PartitionByMethod uint8

const (
	// partition by organization id only
	PartitionByOrg PartitionByMethod = iota

	// partition by the metric name only
	PartitionBySeries

	// partition by metric name and tags, with the best distribution
	// recommended for new deployments.
	PartitionBySeriesWithTags

	// partition by metric name and tags, with a sub-optimal distribution when using tags.
	// compatible with PartitionBySeries if a metric has no tags,
	// making it possible to adopt tags for existing PartitionBySeries deployments without a migration.
	PartitionBySeriesWithTagsFnv
)

func (m *MetricData) PartitionID(method PartitionByMethod, partitions int32) (int32, error) {
	var partition int32

	switch method {
	case PartitionByOrg:
		h := fnv.New32a()
		err := binary.Write(h, binary.LittleEndian, uint32(m.OrgId))
		if err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeries:
		h := fnv.New32a()
		h.Write([]byte(m.Name))
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeriesWithTags:
		h := xxhash.New()
		if err := writeSortedTagString(h, m.Name, m.Tags); err != nil {
			return 0, err
		}
		partition = jump.Hash(h.Sum64(), int(partitions))
	case PartitionBySeriesWithTagsFnv:
		h := fnvNew32aStringWriter()
		if err := writeSortedTagString(h, m.Name, m.Tags); err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	default:
		return 0, ErrUnknownPartitionMethod
	}

	return partition, nil
}

func (m *MetricDefinition) PartitionID(method PartitionByMethod, partitions int32) (int32, error) {
	var partition int32

	switch method {
	case PartitionByOrg:
		h := fnv.New32a()
		err := binary.Write(h, binary.LittleEndian, uint32(m.OrgId))
		if err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeries:
		h := fnv.New32a()
		h.Write([]byte(m.Name))
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeriesWithTags:
		h := xxhash.New()
		h.WriteString(m.NameWithTags())
		partition = jump.Hash(h.Sum64(), int(partitions))
	case PartitionBySeriesWithTagsFnv:
		var h sum32aStringWriter = offset32
		if len(m.nameWithTags) > 0 {
			h.WriteString(m.nameWithTags)
		} else {
			if err := writeSortedTagString(&h, m.Name, m.Tags); err != nil {
				return 0, err
			}
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	default:
		return 0, ErrUnknownPartitionMethod
	}

	return partition, nil
}
<commit_msg>now that we have sum32aStringWriter we can take advantage in other places as well<commit_after>package schema

import (
	"encoding/binary"
	"hash/fnv"

	"github.com/cespare/xxhash"
	jump "github.com/dgryski/go-jump"
)

type PartitionByMethod uint8

const (
	// partition by organization id only
	PartitionByOrg PartitionByMethod = iota

	// partition by the metric name only
	PartitionBySeries

	// partition by metric name and tags, with the best distribution
	// recommended for new deployments.
	PartitionBySeriesWithTags

	// partition by metric name and tags, with a sub-optimal distribution when using tags.
	// compatible with PartitionBySeries if a metric has no tags,
	// making it possible to adopt tags for existing PartitionBySeries deployments without a migration.
	PartitionBySeriesWithTagsFnv
)

func (m *MetricData) PartitionID(method PartitionByMethod, partitions int32) (int32, error) {
	var partition int32

	switch method {
	case PartitionByOrg:
		h := fnv.New32a()
		err := binary.Write(h, binary.LittleEndian, uint32(m.OrgId))
		if err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeries:
		var h sum32aStringWriter = offset32
		h.WriteString(m.Name)
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeriesWithTags:
		h := xxhash.New()
		if err := writeSortedTagString(h, m.Name, m.Tags); err != nil {
			return 0, err
		}
		partition = jump.Hash(h.Sum64(), int(partitions))
	case PartitionBySeriesWithTagsFnv:
		h := fnvNew32aStringWriter()
		if err := writeSortedTagString(h, m.Name, m.Tags); err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	default:
		return 0, ErrUnknownPartitionMethod
	}

	return partition, nil
}

func (m *MetricDefinition) PartitionID(method PartitionByMethod, partitions int32) (int32, error) {
	var partition int32

	switch method {
	case PartitionByOrg:
		h := fnv.New32a()
		err := binary.Write(h, binary.LittleEndian, uint32(m.OrgId))
		if err != nil {
			return 0, err
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeries:
		var h sum32aStringWriter = offset32
		h.WriteString(m.Name)
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	case PartitionBySeriesWithTags:
		h := xxhash.New()
		h.WriteString(m.NameWithTags())
		partition = jump.Hash(h.Sum64(), int(partitions))
	case PartitionBySeriesWithTagsFnv:
		var h sum32aStringWriter = offset32
		if len(m.nameWithTags) > 0 {
			h.WriteString(m.nameWithTags)
		} else {
			if err := writeSortedTagString(&h, m.Name, m.Tags); err != nil {
				return 0, err
			}
		}
		partition = int32(h.Sum32()) % partitions
		if partition < 0 {
			partition = -partition
		}
	default:
		return 0, ErrUnknownPartitionMethod
	}

	return partition, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 schema

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

	digest "github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/xeipuuv/gojsonschema"
)

// Validator wraps a media type string identifier
// and implements validation against a JSON schema.
type Validator string

type validateFunc func(r io.Reader) error

var mapValidate = map[Validator]validateFunc{
	ValidatorMediaTypeImageConfig: validateConfig,
	ValidatorMediaTypeDescriptor:  validateDescriptor,
	ValidatorMediaTypeImageIndex:  validateIndex,
	ValidatorMediaTypeManifest:    validateManifest,
}

// ValidationError contains all the errors that happened during validation.
type ValidationError struct {
	Errs []error
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%v", e.Errs)
}

// Validate validates the given reader against the schema of the wrapped media type.
func (v Validator) Validate(src io.Reader) error {
	buf, err := ioutil.ReadAll(src)
	if err != nil {
		return errors.Wrap(err, "unable to read the document file")
	}

	if f, ok := mapValidate[v]; ok {
		if f == nil {
			return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
		}
		err = f(bytes.NewReader(buf))
		if err != nil {
			return err
		}
	}

	sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
	ml := gojsonschema.NewStringLoader(string(buf))

	result, err := gojsonschema.Validate(sl, ml)
	if err != nil {
		return errors.Wrapf(
			WrapSyntaxError(bytes.NewReader(buf), err),
			"schema %s: unable to validate", v)
	}

	if result.Valid() {
		return nil
	}

	errs := make([]error, 0, len(result.Errors()))
	for _, desc := range result.Errors() {
		errs = append(errs, fmt.Errorf("%s", desc))
	}

	return ValidationError{
		Errs: errs,
	}
}

type unimplemented string

func (v unimplemented) Validate(src io.Reader) error {
	return fmt.Errorf("%s: unimplemented", v)
}

func validateManifest(r io.Reader) error {
	header := v1.Manifest{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifest format mismatch")
	}

	if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
		fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
	}

	for _, layer := range header.Layers {
		if layer.MediaType != string(v1.MediaTypeImageLayer) &&
			layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
			fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
		}
	}
	return nil
}

var (
	sha256EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{64}$`)
	sha512EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{128}$`)
)

func validateDescriptor(r io.Reader) error {
	header := v1.Descriptor{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "descriptor format mismatch")
	}

	if header.Digest.Validate() != nil {
		// we ignore unsupported algorithms
		fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
		return nil
	}
	switch header.Digest.Algorithm() {
	case digest.SHA256:
		if !sha256EncodedRegexp.MatchString(header.Digest.Hex()) {
			return errors.Errorf("unexpected sha256 digest: %q", header.Digest)
		}
	case digest.SHA512:
		if !sha512EncodedRegexp.MatchString(header.Digest.Hex()) {
			return errors.Errorf("unexpected sha512 digest: %q", header.Digest)
		}
	}
	return nil
}

func validateIndex(r io.Reader) error {
	header := v1.Index{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifestlist format mismatch")
	}

	for _, manifest := range header.Manifests {
		if manifest.MediaType != string(v1.MediaTypeImageManifest) {
			fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
		}

	}

	return nil
}

func validateConfig(r io.Reader) error {
	header := v1.Image{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "config format mismatch")
	}

	checkPlatform(header.OS, header.Architecture)

	return nil
}

func checkPlatform(OS string, Architecture string) {
	validCombins := map[string][]string{
		"android":   {"arm"},
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == OS {
			for _, arch := range archs {
				if arch == Architecture {
					return
				}
			}
			fmt.Printf("warning: combination of %q and %q is invalid.", OS, Architecture)
		}
	}
	fmt.Printf("warning: operating system %q of the bundle is not supported yet.", OS)
}
<commit_msg>schema/validator: Punt sha256/sha512 hex-validation to go-digest<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 schema

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

	digest "github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/xeipuuv/gojsonschema"
)

// Validator wraps a media type string identifier
// and implements validation against a JSON schema.
type Validator string

type validateFunc func(r io.Reader) error

var mapValidate = map[Validator]validateFunc{
	ValidatorMediaTypeImageConfig: validateConfig,
	ValidatorMediaTypeDescriptor:  validateDescriptor,
	ValidatorMediaTypeImageIndex:  validateIndex,
	ValidatorMediaTypeManifest:    validateManifest,
}

// ValidationError contains all the errors that happened during validation.
type ValidationError struct {
	Errs []error
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%v", e.Errs)
}

// Validate validates the given reader against the schema of the wrapped media type.
func (v Validator) Validate(src io.Reader) error {
	buf, err := ioutil.ReadAll(src)
	if err != nil {
		return errors.Wrap(err, "unable to read the document file")
	}

	if f, ok := mapValidate[v]; ok {
		if f == nil {
			return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
		}
		err = f(bytes.NewReader(buf))
		if err != nil {
			return err
		}
	}

	sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
	ml := gojsonschema.NewStringLoader(string(buf))

	result, err := gojsonschema.Validate(sl, ml)
	if err != nil {
		return errors.Wrapf(
			WrapSyntaxError(bytes.NewReader(buf), err),
			"schema %s: unable to validate", v)
	}

	if result.Valid() {
		return nil
	}

	errs := make([]error, 0, len(result.Errors()))
	for _, desc := range result.Errors() {
		errs = append(errs, fmt.Errorf("%s", desc))
	}

	return ValidationError{
		Errs: errs,
	}
}

type unimplemented string

func (v unimplemented) Validate(src io.Reader) error {
	return fmt.Errorf("%s: unimplemented", v)
}

func validateManifest(r io.Reader) error {
	header := v1.Manifest{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifest format mismatch")
	}

	if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
		fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
	}

	for _, layer := range header.Layers {
		if layer.MediaType != string(v1.MediaTypeImageLayer) &&
			layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
			fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
		}
	}
	return nil
}

func validateDescriptor(r io.Reader) error {
	header := v1.Descriptor{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "descriptor format mismatch")
	}

	err = header.Digest.Validate()
	if  err == digest.ErrDigestUnsupported {
		// we ignore unsupported algorithms
		fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
		return nil
	}
	return err
}

func validateIndex(r io.Reader) error {
	header := v1.Index{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifestlist format mismatch")
	}

	for _, manifest := range header.Manifests {
		if manifest.MediaType != string(v1.MediaTypeImageManifest) {
			fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
		}

	}

	return nil
}

func validateConfig(r io.Reader) error {
	header := v1.Image{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "config format mismatch")
	}

	checkPlatform(header.OS, header.Architecture)

	return nil
}

func checkPlatform(OS string, Architecture string) {
	validCombins := map[string][]string{
		"android":   {"arm"},
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == OS {
			for _, arch := range archs {
				if arch == Architecture {
					return
				}
			}
			fmt.Printf("warning: combination of %q and %q is invalid.", OS, Architecture)
		}
	}
	fmt.Printf("warning: operating system %q of the bundle is not supported yet.", OS)
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/tidb"
	"github.com/pingcap/tidb/field"
	"github.com/pingcap/tidb/kv"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/rset"
	tidberrors "github.com/pingcap/tidb/util/errors"
	"github.com/pingcap/tidb/util/errors2"
)

// TiDBDriver implements IDriver.
type TiDBDriver struct {
	store kv.Storage
}

// NewTiDBDriver creates a new TiDBDriver.
func NewTiDBDriver(store kv.Storage) *TiDBDriver {
	driver := &TiDBDriver{
		store: store,
	}
	return driver
}

// TiDBContext implements IContext.
type TiDBContext struct {
	session      tidb.Session
	currentDB    string
	warningCount uint16
	stmts        map[int]*TiDBStatement
}

// TiDBStatement implements IStatement.
type TiDBStatement struct {
	id          uint32
	numParams   int
	boundParams [][]byte
	ctx         *TiDBContext
}

// ID implements IStatement ID method.
func (ts *TiDBStatement) ID() int {
	return int(ts.id)
}

// Execute implements IStatement Execute method.
func (ts *TiDBStatement) Execute(args ...interface{}) (rs ResultSet, err error) {
	tidbRecordset, err := ts.ctx.session.ExecutePreparedStmt(ts.id, args...)
	if errors2.ErrorEqual(err, kv.ErrConditionNotMatch) {
		return nil, ts.ctx.session.Retry()
	}
	if err != nil {
		return nil, err
	}
	if tidbRecordset == nil {
		return
	}
	rs = &tidbResultSet{
		recordSet: tidbRecordset,
	}
	return
}

// AppendParam implements IStatement AppendParam method.
func (ts *TiDBStatement) AppendParam(paramID int, data []byte) error {
	if paramID >= len(ts.boundParams) {
		return mysql.NewDefaultError(mysql.ErWrongArguments, "stmt_send_longdata")
	}
	ts.boundParams[paramID] = append(ts.boundParams[paramID], data...)
	return nil
}

// NumParams implements IStatement NumParams method.
func (ts *TiDBStatement) NumParams() int {
	return ts.numParams
}

// BoundParams implements IStatement BoundParams method.
func (ts *TiDBStatement) BoundParams() [][]byte {
	return ts.boundParams
}

// Reset implements IStatement Reset method.
func (ts *TiDBStatement) Reset() {
	for i := range ts.boundParams {
		ts.boundParams[i] = nil
	}
}

// Close implements IStatement Close method.
func (ts *TiDBStatement) Close() error {
	//TODO close at tidb level
	err := ts.ctx.session.DropPreparedStmt(ts.id)
	if err != nil {
		return errors.Trace(err)
	}
	delete(ts.ctx.stmts, int(ts.id))
	return nil
}

// OpenCtx implements IDriver.
func (qd *TiDBDriver) OpenCtx(capability uint32, collation uint8, dbname string) (IContext, error) {
	session, _ := tidb.CreateSession(qd.store)
	session.SetClientCapability(capability)
	if dbname != "" {
		_, err := session.Execute("use " + dbname)
		if err != nil {
			return nil, err
		}
	}
	tc := &TiDBContext{
		session:   session,
		currentDB: dbname,
		stmts:     make(map[int]*TiDBStatement),
	}
	return tc, nil
}

// Status implements IContext Status method.
func (tc *TiDBContext) Status() uint16 {
	return tc.session.Status()
}

// LastInsertID implements IContext LastInsertID method.
func (tc *TiDBContext) LastInsertID() uint64 {
	return tc.session.LastInsertID()
}

// AffectedRows implements IContext AffectedRows method.
func (tc *TiDBContext) AffectedRows() uint64 {
	return tc.session.AffectedRows()
}

// CurrentDB implements IContext CurrentDB method.
func (tc *TiDBContext) CurrentDB() string {
	return tc.currentDB
}

// WarningCount implements IContext WarningCount method.
func (tc *TiDBContext) WarningCount() uint16 {
	return tc.warningCount
}

// Execute implements IContext Execute method.
func (tc *TiDBContext) Execute(sql string) (rs ResultSet, err error) {
	rsList, err := tc.session.Execute(sql)
	if errors2.ErrorEqual(err, kv.ErrConditionNotMatch) {
		return nil, tc.session.Retry()
	}
	if err != nil {
		return
	}
	if len(rsList) == 0 { // result ok
		return
	}
	rs = &tidbResultSet{
		recordSet: rsList[0],
	}
	return
}

// Close implements IContext Close method.
func (tc *TiDBContext) Close() (err error) {
	return tc.session.Close()
}

// FieldList implements IContext FieldList method.
func (tc *TiDBContext) FieldList(table string) (colums []*ColumnInfo, err error) {
	rs, err := tc.Execute("SELECT * FROM " + table + " LIMIT 0")
	if err != nil {
		return nil, errors.Trace(err)
	}
	colums, err = rs.Columns()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return
}

// GetStatement implements IContext GetStatement method.
func (tc *TiDBContext) GetStatement(stmtID int) IStatement {
	tcStmt := tc.stmts[stmtID]
	if tcStmt != nil {
		return tcStmt
	}
	return nil
}

// Prepare implements IContext Prepare method.
func (tc *TiDBContext) Prepare(sql string) (statement IStatement, columns, params []*ColumnInfo, err error) {
	stmtID, paramCount, fields, err := tc.session.PrepareStmt(sql)
	if err != nil {
		return
	}
	stmt := &TiDBStatement{
		id:          stmtID,
		numParams:   paramCount,
		boundParams: make([][]byte, paramCount),
		ctx:         tc,
	}
	statement = stmt
	columns = make([]*ColumnInfo, len(fields))
	for i := range fields {
		columns[i] = convertColumnInfo(fields[i])
	}
	params = make([]*ColumnInfo, paramCount)
	for i := range params {
		params[i] = &ColumnInfo{
			Type: mysql.TypeBlob,
		}
	}
	tc.stmts[int(stmtID)] = stmt
	return
}

type tidbResultSet struct {
	recordSet rset.Recordset
}

func (trs *tidbResultSet) Next() ([]interface{}, error) {
	row, err := trs.recordSet.Next()
	if err != nil {
		return nil, errors.Trace(err)
	}
	if row != nil {
		return row.Data, nil
	}
	return nil, nil
}

func (trs *tidbResultSet) Close() error {
	return trs.recordSet.Close()
}

func (trs *tidbResultSet) Columns() ([]*ColumnInfo, error) {
	fields, err := trs.recordSet.Fields()
	if err != nil {
		return nil, errors.Trace(err)
	}
	var columns []*ColumnInfo
	for _, v := range fields {
		columns = append(columns, convertColumnInfo(v))
	}
	return columns, nil
}

func convertColumnInfo(fld *field.ResultField) (ci *ColumnInfo) {
	ci = new(ColumnInfo)
	ci.Name = fld.Name
	ci.OrgName = fld.ColumnInfo.Name.O
	ci.Table = fld.TableName
	ci.OrgTable = fld.OrgTableName
	ci.Schema = fld.DBName
	ci.Flag = uint16(fld.Flag)
	ci.Charset = uint16(mysql.CharsetIDs[fld.Charset])
	ci.ColumnLength = uint32(fld.Flen)
	ci.Decimal = uint8(fld.Decimal)
	ci.Type = uint8(fld.Tp)
	return
}

// Bootstrap initiate TiDB server
func Bootstrap(store kv.Storage) {
	td := NewTiDBDriver(store)
	tc, err := td.OpenCtx(defaultCapability, mysql.DefaultCollationID, "")
	defer tc.Close()
	if err != nil {
		log.Fatal(err)
	}
	// Create a test database.
	_, err = tc.Execute("CREATE DATABASE IF NOT EXISTS test")
	if err != nil {
		log.Fatal(err)
	}

	//  Check if mysql db exists.
	_, err = tc.Execute("USE mysql;")
	if err == nil {
		// Already bootstraped
		return
	} else if !errors2.ErrorEqual(err, tidberrors.ErrDatabaseNotExist) {
		log.Fatal(err)
	}
	_, err = tc.Execute("CREATE DATABASE mysql;")
	if err != nil {
		log.Fatal(err)
	}
	_, err = tc.Execute("CREATE TABLE mysql.user (Host CHAR(64), User CHAR(16), Password CHAR(41), PRIMARY KEY (Host, User));")
	if err != nil {
		log.Fatal(err)
	}
	// Insert a default user with empty password.
	_, err = tc.Execute(`INSERT INTO mysql.user VALUES ("localhost", "root", ""), ("127.0.0.1", "root", "");`)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>tidb-server: Address comment<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/tidb"
	"github.com/pingcap/tidb/field"
	"github.com/pingcap/tidb/kv"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/rset"
	tidberrors "github.com/pingcap/tidb/util/errors"
	"github.com/pingcap/tidb/util/errors2"
)

// TiDBDriver implements IDriver.
type TiDBDriver struct {
	store kv.Storage
}

// NewTiDBDriver creates a new TiDBDriver.
func NewTiDBDriver(store kv.Storage) *TiDBDriver {
	driver := &TiDBDriver{
		store: store,
	}
	return driver
}

// TiDBContext implements IContext.
type TiDBContext struct {
	session      tidb.Session
	currentDB    string
	warningCount uint16
	stmts        map[int]*TiDBStatement
}

// TiDBStatement implements IStatement.
type TiDBStatement struct {
	id          uint32
	numParams   int
	boundParams [][]byte
	ctx         *TiDBContext
}

// ID implements IStatement ID method.
func (ts *TiDBStatement) ID() int {
	return int(ts.id)
}

// Execute implements IStatement Execute method.
func (ts *TiDBStatement) Execute(args ...interface{}) (rs ResultSet, err error) {
	tidbRecordset, err := ts.ctx.session.ExecutePreparedStmt(ts.id, args...)
	if errors2.ErrorEqual(err, kv.ErrConditionNotMatch) {
		return nil, ts.ctx.session.Retry()
	}
	if err != nil {
		return nil, err
	}
	if tidbRecordset == nil {
		return
	}
	rs = &tidbResultSet{
		recordSet: tidbRecordset,
	}
	return
}

// AppendParam implements IStatement AppendParam method.
func (ts *TiDBStatement) AppendParam(paramID int, data []byte) error {
	if paramID >= len(ts.boundParams) {
		return mysql.NewDefaultError(mysql.ErWrongArguments, "stmt_send_longdata")
	}
	ts.boundParams[paramID] = append(ts.boundParams[paramID], data...)
	return nil
}

// NumParams implements IStatement NumParams method.
func (ts *TiDBStatement) NumParams() int {
	return ts.numParams
}

// BoundParams implements IStatement BoundParams method.
func (ts *TiDBStatement) BoundParams() [][]byte {
	return ts.boundParams
}

// Reset implements IStatement Reset method.
func (ts *TiDBStatement) Reset() {
	for i := range ts.boundParams {
		ts.boundParams[i] = nil
	}
}

// Close implements IStatement Close method.
func (ts *TiDBStatement) Close() error {
	//TODO close at tidb level
	err := ts.ctx.session.DropPreparedStmt(ts.id)
	if err != nil {
		return errors.Trace(err)
	}
	delete(ts.ctx.stmts, int(ts.id))
	return nil
}

// OpenCtx implements IDriver.
func (qd *TiDBDriver) OpenCtx(capability uint32, collation uint8, dbname string) (IContext, error) {
	session, _ := tidb.CreateSession(qd.store)
	session.SetClientCapability(capability)
	if dbname != "" {
		_, err := session.Execute("use " + dbname)
		if err != nil {
			return nil, err
		}
	}
	tc := &TiDBContext{
		session:   session,
		currentDB: dbname,
		stmts:     make(map[int]*TiDBStatement),
	}
	return tc, nil
}

// Status implements IContext Status method.
func (tc *TiDBContext) Status() uint16 {
	return tc.session.Status()
}

// LastInsertID implements IContext LastInsertID method.
func (tc *TiDBContext) LastInsertID() uint64 {
	return tc.session.LastInsertID()
}

// AffectedRows implements IContext AffectedRows method.
func (tc *TiDBContext) AffectedRows() uint64 {
	return tc.session.AffectedRows()
}

// CurrentDB implements IContext CurrentDB method.
func (tc *TiDBContext) CurrentDB() string {
	return tc.currentDB
}

// WarningCount implements IContext WarningCount method.
func (tc *TiDBContext) WarningCount() uint16 {
	return tc.warningCount
}

// Execute implements IContext Execute method.
func (tc *TiDBContext) Execute(sql string) (rs ResultSet, err error) {
	rsList, err := tc.session.Execute(sql)
	if errors2.ErrorEqual(err, kv.ErrConditionNotMatch) {
		return nil, tc.session.Retry()
	}
	if err != nil {
		return
	}
	if len(rsList) == 0 { // result ok
		return
	}
	rs = &tidbResultSet{
		recordSet: rsList[0],
	}
	return
}

// Close implements IContext Close method.
func (tc *TiDBContext) Close() (err error) {
	return tc.session.Close()
}

// FieldList implements IContext FieldList method.
func (tc *TiDBContext) FieldList(table string) (colums []*ColumnInfo, err error) {
	rs, err := tc.Execute("SELECT * FROM " + table + " LIMIT 0")
	if err != nil {
		return nil, errors.Trace(err)
	}
	colums, err = rs.Columns()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return
}

// GetStatement implements IContext GetStatement method.
func (tc *TiDBContext) GetStatement(stmtID int) IStatement {
	tcStmt := tc.stmts[stmtID]
	if tcStmt != nil {
		return tcStmt
	}
	return nil
}

// Prepare implements IContext Prepare method.
func (tc *TiDBContext) Prepare(sql string) (statement IStatement, columns, params []*ColumnInfo, err error) {
	stmtID, paramCount, fields, err := tc.session.PrepareStmt(sql)
	if err != nil {
		return
	}
	stmt := &TiDBStatement{
		id:          stmtID,
		numParams:   paramCount,
		boundParams: make([][]byte, paramCount),
		ctx:         tc,
	}
	statement = stmt
	columns = make([]*ColumnInfo, len(fields))
	for i := range fields {
		columns[i] = convertColumnInfo(fields[i])
	}
	params = make([]*ColumnInfo, paramCount)
	for i := range params {
		params[i] = &ColumnInfo{
			Type: mysql.TypeBlob,
		}
	}
	tc.stmts[int(stmtID)] = stmt
	return
}

type tidbResultSet struct {
	recordSet rset.Recordset
}

func (trs *tidbResultSet) Next() ([]interface{}, error) {
	row, err := trs.recordSet.Next()
	if err != nil {
		return nil, errors.Trace(err)
	}
	if row != nil {
		return row.Data, nil
	}
	return nil, nil
}

func (trs *tidbResultSet) Close() error {
	return trs.recordSet.Close()
}

func (trs *tidbResultSet) Columns() ([]*ColumnInfo, error) {
	fields, err := trs.recordSet.Fields()
	if err != nil {
		return nil, errors.Trace(err)
	}
	var columns []*ColumnInfo
	for _, v := range fields {
		columns = append(columns, convertColumnInfo(v))
	}
	return columns, nil
}

func convertColumnInfo(fld *field.ResultField) (ci *ColumnInfo) {
	ci = new(ColumnInfo)
	ci.Name = fld.Name
	ci.OrgName = fld.ColumnInfo.Name.O
	ci.Table = fld.TableName
	ci.OrgTable = fld.OrgTableName
	ci.Schema = fld.DBName
	ci.Flag = uint16(fld.Flag)
	ci.Charset = uint16(mysql.CharsetIDs[fld.Charset])
	ci.ColumnLength = uint32(fld.Flen)
	ci.Decimal = uint8(fld.Decimal)
	ci.Type = uint8(fld.Tp)
	return
}

// Bootstrap initiates TiDB server.
func Bootstrap(store kv.Storage) {
	td := NewTiDBDriver(store)
	tc, err := td.OpenCtx(defaultCapability, mysql.DefaultCollationID, "")
	defer tc.Close()
	if err != nil {
		log.Fatal(err)
	}
	// Create a test database.
	_, err = tc.Execute("CREATE DATABASE IF NOT EXISTS test")
	if err != nil {
		log.Fatal(err)
	}

	//  Check if mysql db exists.
	_, err = tc.Execute("USE mysql;")
	if err == nil {
		// Already bootstrapeds
		return
	} else if !errors2.ErrorEqual(err, tidberrors.ErrDatabaseNotExist) {
		log.Fatal(err)
	}
	_, err = tc.Execute("CREATE DATABASE mysql;")
	if err != nil {
		log.Fatal(err)
	}
	_, err = tc.Execute("CREATE TABLE mysql.user (Host CHAR(64), User CHAR(16), Password CHAR(41), PRIMARY KEY (Host, User));")
	if err != nil {
		log.Fatal(err)
	}
	// Insert a default user with empty password.
	_, err = tc.Execute(`INSERT INTO mysql.user VALUES ("localhost", "root", ""), ("127.0.0.1", "root", "");`)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Package test_util provides utilities useful for bigquery
package test_util

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"

	"golang.org/x/oauth2/google"
)

// TODO(gfr) Move these to an http-util package in another repo?
type loggingTransport struct {
	Transport http.RoundTripper
}

type nopCloser struct {
	io.Reader
}

func (nc *nopCloser) Close() error { return nil }

// Log the contents of a reader, returning a new reader with
// same content.
func loggingReader(r io.ReadCloser) io.ReadCloser {
	buf, _ := ioutil.ReadAll(r)
	r.Close()
	log.Printf("Response body:\n%+v\n", string(buf))
	return &nopCloser{bytes.NewReader(buf)}
}

// RoundTrip implements the RoundTripper interface, logging the
// request, and the response body, (which may be json).
func (t loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Using %#v results in an escaped string we can use in code.
	log.Printf("Request:\n%#v\n", req)
	fmt.Println(t)
	var resp *http.Response
	var err error
	if t.Transport == nil {
		resp, err = http.DefaultTransport.RoundTrip(req)

	} else {
		resp, err = t.Transport.RoundTrip(req)
	}
	resp.Body = loggingReader(resp.Body)
	return resp, err
}

// LoggingClient is an HTTP client that also logs all requests and
// responses.
// TODO(gfr) Add support for an arbitrary logger.
func LoggingClient(client *http.Client) (*http.Client, error) {
	if client == nil {
		var err error
		ctx := context.Background()
		client, err = google.DefaultClient(ctx, "https://www.googleapis.com/auth/bigquery")
		if err != nil {
			return nil, err
		}
	} else {
		if client == http.DefaultClient {
			log.Fatal("Bad idea to add logging to default client")
		}
	}

	fmt.Println(client)
	client.Transport = &loggingTransport{client.Transport}
	fmt.Println(client)

	return client, nil
}

// channelTransport provides a RoundTripper that handles everything
// locally.
type channelTransport struct {
	//	Transport http.RoundTripper
	Responses <-chan *http.Response
}

// RoundTrip implements the RoundTripper interface, using a channel to
// provide http responses.  This will block if the channel is empty.
func (t channelTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	resp := <-t.Responses // may block
	resp.Request = req
	return resp, nil
}

// ChannelClient is an HTTP client that ignores requests and returns
// responses provided by a channel.
// responses.
func ChannelClient(c <-chan *http.Response) *http.Client {
	client := &http.Client{}
	client.Transport = &channelTransport{c}

	return client
}
<commit_msg>Fix nil Transport bug<commit_after>// Package test_util provides utilities useful for bigquery
package test_util

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

// TODO(gfr) Move these to an http-util package in another repo?
type loggingTransport struct {
	Transport http.RoundTripper
}

type nopCloser struct {
	io.Reader
}

func (nc *nopCloser) Close() error { return nil }

// Log the contents of a reader, returning a new reader with
// same content.
func loggingReader(r io.ReadCloser) io.ReadCloser {
	buf, _ := ioutil.ReadAll(r)
	r.Close()
	log.Printf("Response body:\n%+v\n", string(buf))
	return &nopCloser{bytes.NewReader(buf)}
}

// RoundTrip implements the RoundTripper interface, logging the
// request, and the response body, (which may be json).
func (t loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Using %#v results in an escaped string we can use in code.
	log.Printf("Request:\n%#v\n", req)
	var resp *http.Response
	var err error
	// nil Transport is valid, so check for it.
	if t.Transport == nil {
		resp, err = http.DefaultTransport.RoundTrip(req)

	} else {
		resp, err = t.Transport.RoundTrip(req)
	}
	resp.Body = loggingReader(resp.Body)
	return resp, err
}

// LoggingClient is an HTTP client that also logs all requests and
// responses.
func LoggingClient(client *http.Client) (*http.Client, error) {
	if client == nil {
		client = &http.Client{}
	} else {
		if client == http.DefaultClient {
			log.Fatal("Bad idea to add logging to default client")
		}
	}

	client.Transport = &loggingTransport{client.Transport}
	return client, nil
}

// channelTransport provides a RoundTripper that handles everything
// locally.
type channelTransport struct {
	//	Transport http.RoundTripper
	Responses <-chan *http.Response
}

// RoundTrip implements the RoundTripper interface, using a channel to
// provide http responses.  This will block if the channel is empty.
func (t channelTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	resp := <-t.Responses // may block
	resp.Request = req
	return resp, nil
}

// ChannelClient is an HTTP client that ignores requests and returns
// responses provided by a channel.
// responses.
func ChannelClient(c <-chan *http.Response) *http.Client {
	client := &http.Client{}
	client.Transport = &channelTransport{c}

	return client
}
<|endoftext|>


<commit_before>package influxdb

import (
	"errors"
	"fmt"
	"path/filepath"
)

var (
	// ErrAuthorizerNotSupported notes that the provided authorizer is not supported for the action you are trying to perform.
	ErrAuthorizerNotSupported = errors.New("your authorizer is not supported, please use *platform.Authorization as authorizer")
	// ErrInvalidResourceType notes that the provided resource is invalid
	ErrInvalidResourceType = errors.New("unknown resource type for permission")
	// ErrInvalidAction notes that the provided action is invalid
	ErrInvalidAction = errors.New("unknown action for permission")
)

// Authorizer will authorize a permission.
type Authorizer interface {
	// PermissionSet returns the PermissionSet associated with the authorizer
	PermissionSet() (PermissionSet, error)

	// ID returns an identifier used for auditing.
	Identifier() ID

	// GetUserID returns the user id.
	GetUserID() ID

	// Kind metadata for auditing.
	Kind() string
}

// PermissionAllowed determines if a permission is allowed.
func PermissionAllowed(perm Permission, ps []Permission) bool {
	for _, p := range ps {
		if p.Matches(perm) {
			return true
		}
	}
	return false
}

// Action is an enum defining all possible resource operations
type Action string

const (
	// ReadAction is the action for reading.
	ReadAction Action = "read" // 1
	// WriteAction is the action for writing.
	WriteAction Action = "write" // 2
)

var actions = []Action{
	ReadAction,  // 1
	WriteAction, // 2
}

// Valid checks if the action is a member of the Action enum
func (a Action) Valid() (err error) {
	switch a {
	case ReadAction: // 1
	case WriteAction: // 2
	default:
		err = ErrInvalidAction
	}

	return err
}

// ResourceType is an enum defining all resource types that have a permission model in platform
type ResourceType string

// Resource is an authorizable resource.
type Resource struct {
	Type  ResourceType `json:"type"`
	ID    *ID          `json:"id,omitempty"`
	OrgID *ID          `json:"orgID,omitempty"`
}

// String stringifies a resource
func (r Resource) String() string {
	if r.OrgID != nil && r.ID != nil {
		return filepath.Join(string(OrgsResourceType), r.OrgID.String(), string(r.Type), r.ID.String())
	}

	if r.OrgID != nil {
		return filepath.Join(string(OrgsResourceType), r.OrgID.String(), string(r.Type))
	}

	if r.ID != nil {
		return filepath.Join(string(r.Type), r.ID.String())
	}

	return string(r.Type)
}

const (
	// AuthorizationsResourceType gives permissions to one or more authorizations.
	AuthorizationsResourceType = ResourceType("authorizations") // 0
	// BucketsResourceType gives permissions to one or more buckets.
	BucketsResourceType = ResourceType("buckets") // 1
	// DashboardsResourceType gives permissions to one or more dashboards.
	DashboardsResourceType = ResourceType("dashboards") // 2
	// OrgsResourceType gives permissions to one or more orgs.
	OrgsResourceType = ResourceType("orgs") // 3
	// SourcesResourceType gives permissions to one or more sources.
	SourcesResourceType = ResourceType("sources") // 4
	// TasksResourceType gives permissions to one or more tasks.
	TasksResourceType = ResourceType("tasks") // 5
	// TelegrafsResourceType type gives permissions to a one or more telegrafs.
	TelegrafsResourceType = ResourceType("telegrafs") // 6
	// UsersResourceType gives permissions to one or more users.
	UsersResourceType = ResourceType("users") // 7
	// VariablesResourceType gives permission to one or more variables.
	VariablesResourceType = ResourceType("variables") // 8
	// ScraperResourceType gives permission to one or more scrapers.
	ScraperResourceType = ResourceType("scrapers") // 9
	// SecretsResourceType gives permission to one or more secrets.
	SecretsResourceType = ResourceType("secrets") // 10
	// LabelsResourceType gives permission to one or more labels.
	LabelsResourceType = ResourceType("labels") // 11
	// ViewsResourceType gives permission to one or more views.
	ViewsResourceType = ResourceType("views") // 12
	// DocumentsResourceType gives permission to one or more documents.
	DocumentsResourceType = ResourceType("documents") // 13
	// NotificationRuleResourceType gives permission to one or more notificationRules.
	NotificationRuleResourceType = ResourceType("notificationRules") // 14
	// NotificationEndpointResourceType gives permission to one or more notificationEndpoints.
	NotificationEndpointResourceType = ResourceType("notificationEndpoints") // 15
	// ChecksResourceType gives permission to one or more Checks.
	ChecksResourceType = ResourceType("checks") // 16
	// DBRPType gives permission to one or more DBRPs.
	DBRPResourceType = ResourceType("dbrp") // 17
)

// AllResourceTypes is the list of all known resource types.
var AllResourceTypes = []ResourceType{
	AuthorizationsResourceType,       // 0
	BucketsResourceType,              // 1
	DashboardsResourceType,           // 2
	OrgsResourceType,                 // 3
	SourcesResourceType,              // 4
	TasksResourceType,                // 5
	TelegrafsResourceType,            // 6
	UsersResourceType,                // 7
	VariablesResourceType,            // 8
	ScraperResourceType,              // 9
	SecretsResourceType,              // 10
	LabelsResourceType,               // 11
	ViewsResourceType,                // 12
	DocumentsResourceType,            // 13
	NotificationRuleResourceType,     // 14
	NotificationEndpointResourceType, // 15
	ChecksResourceType,               // 16
	DBRPResourceType,                 // 17
	// NOTE: when modifying this list, please update the swagger for components.schemas.Permission resource enum.
}

// OrgResourceTypes is the list of all known resource types that belong to an organization.
var OrgResourceTypes = []ResourceType{
	BucketsResourceType,              // 1
	DashboardsResourceType,           // 2
	SourcesResourceType,              // 4
	TasksResourceType,                // 5
	TelegrafsResourceType,            // 6
	UsersResourceType,                // 7
	VariablesResourceType,            // 8
	SecretsResourceType,              // 10
	DocumentsResourceType,            // 13
	NotificationRuleResourceType,     // 14
	NotificationEndpointResourceType, // 15
	ChecksResourceType,               // 16
	DBRPResourceType,                 // 17
}

// Valid checks if the resource type is a member of the ResourceType enum.
func (r Resource) Valid() (err error) {
	return r.Type.Valid()
}

// Valid checks if the resource type is a member of the ResourceType enum.
func (t ResourceType) Valid() (err error) {
	switch t {
	case AuthorizationsResourceType: // 0
	case BucketsResourceType: // 1
	case DashboardsResourceType: // 2
	case OrgsResourceType: // 3
	case TasksResourceType: // 4
	case TelegrafsResourceType: // 5
	case SourcesResourceType: // 6
	case UsersResourceType: // 7
	case VariablesResourceType: // 8
	case ScraperResourceType: // 9
	case SecretsResourceType: // 10
	case LabelsResourceType: // 11
	case ViewsResourceType: // 12
	case DocumentsResourceType: // 13
	case NotificationRuleResourceType: // 14
	case NotificationEndpointResourceType: // 15
	case ChecksResourceType: // 16
	case DBRPResourceType: // 17
	default:
		err = ErrInvalidResourceType
	}

	return err
}

type PermissionSet []Permission

func (ps PermissionSet) Allowed(p Permission) bool {
	return PermissionAllowed(p, ps)
}

// Permission defines an action and a resource.
type Permission struct {
	Action   Action   `json:"action"`
	Resource Resource `json:"resource"`
}

// Matches returns whether or not one permission matches the other.
func (p Permission) Matches(perm Permission) bool {
	if p.Action != perm.Action {
		return false
	}

	if p.Resource.Type != perm.Resource.Type {
		return false
	}

	if p.Resource.OrgID == nil && p.Resource.ID == nil {
		return true
	}

	if p.Resource.OrgID != nil && p.Resource.ID == nil {
		pOrgID := *p.Resource.OrgID
		if perm.Resource.OrgID != nil {
			permOrgID := *perm.Resource.OrgID
			if pOrgID == permOrgID {
				return true
			}
		}
	}

	if p.Resource.ID != nil {
		pID := *p.Resource.ID
		if perm.Resource.ID != nil {
			permID := *perm.Resource.ID
			if pID == permID {
				return true
			}
		}
	}

	return false
}

func (p Permission) String() string {
	return fmt.Sprintf("%s:%s", p.Action, p.Resource)
}

// Valid checks if there the resource and action provided is known.
func (p *Permission) Valid() error {
	if err := p.Resource.Valid(); err != nil {
		return &Error{
			Code: EInvalid,
			Err:  err,
			Msg:  "invalid resource type for permission",
		}
	}

	if err := p.Action.Valid(); err != nil {
		return &Error{
			Code: EInvalid,
			Err:  err,
			Msg:  "invalid action type for permission",
		}
	}

	if p.Resource.OrgID != nil && !p.Resource.OrgID.Valid() {
		return &Error{
			Code: EInvalid,
			Err:  ErrInvalidID,
			Msg:  "invalid org id for permission",
		}
	}

	if p.Resource.ID != nil && !p.Resource.ID.Valid() {
		return &Error{
			Code: EInvalid,
			Err:  ErrInvalidID,
			Msg:  "invalid id for permission",
		}
	}

	return nil
}

// NewPermission returns a permission with provided arguments.
func NewPermission(a Action, rt ResourceType, orgID ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type:  rt,
			OrgID: &orgID,
		},
	}

	return p, p.Valid()
}

// NewResourcePermission returns a permission with provided arguments.
func NewResourcePermission(a Action, rt ResourceType, rid ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type: rt,
			ID:   &rid,
		},
	}

	return p, p.Valid()
}

// NewGlobalPermission constructs a global permission capable of accessing any resource of type rt.
func NewGlobalPermission(a Action, rt ResourceType) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type: rt,
		},
	}
	return p, p.Valid()
}

// NewPermissionAtID creates a permission with the provided arguments.
func NewPermissionAtID(id ID, a Action, rt ResourceType, orgID ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type:  rt,
			OrgID: &orgID,
			ID:    &id,
		},
	}

	return p, p.Valid()
}

// OperPermissions are the default permissions for those who setup the application.
func OperPermissions() []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		for _, a := range actions {
			ps = append(ps, Permission{Action: a, Resource: Resource{Type: r}})
		}
	}

	return ps
}

// ReadAllPermissions represents permission to read all data and metadata.
// Like OperPermissions, but allows read-only users.
func ReadAllPermissions() []Permission {
	ps := make([]Permission, len(AllResourceTypes))
	for i, t := range AllResourceTypes {
		ps[i] = Permission{Action: ReadAction, Resource: Resource{Type: t}}
	}
	return ps
}

// OwnerPermissions are the default permissions for those who own a resource.
func OwnerPermissions(orgID ID) []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		for _, a := range actions {
			if r == OrgsResourceType {
				ps = append(ps, Permission{Action: a, Resource: Resource{Type: r, ID: &orgID}})
				continue
			}
			ps = append(ps, Permission{Action: a, Resource: Resource{Type: r, OrgID: &orgID}})
		}
	}
	return ps
}

// MePermissions is the permission to read/write myself.
func MePermissions(userID ID) []Permission {
	ps := []Permission{}
	for _, a := range actions {
		ps = append(ps, Permission{Action: a, Resource: Resource{Type: UsersResourceType, ID: &userID}})
	}

	return ps
}

// MemberPermissions are the default permissions for those who can see a resource.
func MemberPermissions(orgID ID) []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		if r == OrgsResourceType {
			ps = append(ps, Permission{Action: ReadAction, Resource: Resource{Type: r, ID: &orgID}})
			continue
		}
		ps = append(ps, Permission{Action: ReadAction, Resource: Resource{Type: r, OrgID: &orgID}})
	}

	return ps
}

// MemberPermissions are the default permissions for those who can see a resource.
func MemberBucketPermission(bucketID ID) Permission {
	return Permission{Action: ReadAction, Resource: Resource{Type: BucketsResourceType, ID: &bucketID}}
}
<commit_msg>chore(auth): new match behavior (#19306)<commit_after>package influxdb

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
)

var (
	// ErrAuthorizerNotSupported notes that the provided authorizer is not supported for the action you are trying to perform.
	ErrAuthorizerNotSupported = errors.New("your authorizer is not supported, please use *platform.Authorization as authorizer")
	// ErrInvalidResourceType notes that the provided resource is invalid
	ErrInvalidResourceType = errors.New("unknown resource type for permission")
	// ErrInvalidAction notes that the provided action is invalid
	ErrInvalidAction = errors.New("unknown action for permission")
)

// Authorizer will authorize a permission.
type Authorizer interface {
	// PermissionSet returns the PermissionSet associated with the authorizer
	PermissionSet() (PermissionSet, error)

	// ID returns an identifier used for auditing.
	Identifier() ID

	// GetUserID returns the user id.
	GetUserID() ID

	// Kind metadata for auditing.
	Kind() string
}

// PermissionAllowed determines if a permission is allowed.
func PermissionAllowed(perm Permission, ps []Permission) bool {
	for _, p := range ps {
		if p.Matches(perm) {
			return true
		}
	}
	return false
}

// Action is an enum defining all possible resource operations
type Action string

const (
	// ReadAction is the action for reading.
	ReadAction Action = "read" // 1
	// WriteAction is the action for writing.
	WriteAction Action = "write" // 2
)

var actions = []Action{
	ReadAction,  // 1
	WriteAction, // 2
}

// Valid checks if the action is a member of the Action enum
func (a Action) Valid() (err error) {
	switch a {
	case ReadAction: // 1
	case WriteAction: // 2
	default:
		err = ErrInvalidAction
	}

	return err
}

// ResourceType is an enum defining all resource types that have a permission model in platform
type ResourceType string

// Resource is an authorizable resource.
type Resource struct {
	Type  ResourceType `json:"type"`
	ID    *ID          `json:"id,omitempty"`
	OrgID *ID          `json:"orgID,omitempty"`
}

// String stringifies a resource
func (r Resource) String() string {
	if r.OrgID != nil && r.ID != nil {
		return filepath.Join(string(OrgsResourceType), r.OrgID.String(), string(r.Type), r.ID.String())
	}

	if r.OrgID != nil {
		return filepath.Join(string(OrgsResourceType), r.OrgID.String(), string(r.Type))
	}

	if r.ID != nil {
		return filepath.Join(string(r.Type), r.ID.String())
	}

	return string(r.Type)
}

const (
	// AuthorizationsResourceType gives permissions to one or more authorizations.
	AuthorizationsResourceType = ResourceType("authorizations") // 0
	// BucketsResourceType gives permissions to one or more buckets.
	BucketsResourceType = ResourceType("buckets") // 1
	// DashboardsResourceType gives permissions to one or more dashboards.
	DashboardsResourceType = ResourceType("dashboards") // 2
	// OrgsResourceType gives permissions to one or more orgs.
	OrgsResourceType = ResourceType("orgs") // 3
	// SourcesResourceType gives permissions to one or more sources.
	SourcesResourceType = ResourceType("sources") // 4
	// TasksResourceType gives permissions to one or more tasks.
	TasksResourceType = ResourceType("tasks") // 5
	// TelegrafsResourceType type gives permissions to a one or more telegrafs.
	TelegrafsResourceType = ResourceType("telegrafs") // 6
	// UsersResourceType gives permissions to one or more users.
	UsersResourceType = ResourceType("users") // 7
	// VariablesResourceType gives permission to one or more variables.
	VariablesResourceType = ResourceType("variables") // 8
	// ScraperResourceType gives permission to one or more scrapers.
	ScraperResourceType = ResourceType("scrapers") // 9
	// SecretsResourceType gives permission to one or more secrets.
	SecretsResourceType = ResourceType("secrets") // 10
	// LabelsResourceType gives permission to one or more labels.
	LabelsResourceType = ResourceType("labels") // 11
	// ViewsResourceType gives permission to one or more views.
	ViewsResourceType = ResourceType("views") // 12
	// DocumentsResourceType gives permission to one or more documents.
	DocumentsResourceType = ResourceType("documents") // 13
	// NotificationRuleResourceType gives permission to one or more notificationRules.
	NotificationRuleResourceType = ResourceType("notificationRules") // 14
	// NotificationEndpointResourceType gives permission to one or more notificationEndpoints.
	NotificationEndpointResourceType = ResourceType("notificationEndpoints") // 15
	// ChecksResourceType gives permission to one or more Checks.
	ChecksResourceType = ResourceType("checks") // 16
	// DBRPType gives permission to one or more DBRPs.
	DBRPResourceType = ResourceType("dbrp") // 17
)

// AllResourceTypes is the list of all known resource types.
var AllResourceTypes = []ResourceType{
	AuthorizationsResourceType,       // 0
	BucketsResourceType,              // 1
	DashboardsResourceType,           // 2
	OrgsResourceType,                 // 3
	SourcesResourceType,              // 4
	TasksResourceType,                // 5
	TelegrafsResourceType,            // 6
	UsersResourceType,                // 7
	VariablesResourceType,            // 8
	ScraperResourceType,              // 9
	SecretsResourceType,              // 10
	LabelsResourceType,               // 11
	ViewsResourceType,                // 12
	DocumentsResourceType,            // 13
	NotificationRuleResourceType,     // 14
	NotificationEndpointResourceType, // 15
	ChecksResourceType,               // 16
	DBRPResourceType,                 // 17
	// NOTE: when modifying this list, please update the swagger for components.schemas.Permission resource enum.
}

// OrgResourceTypes is the list of all known resource types that belong to an organization.
var OrgResourceTypes = []ResourceType{
	BucketsResourceType,              // 1
	DashboardsResourceType,           // 2
	SourcesResourceType,              // 4
	TasksResourceType,                // 5
	TelegrafsResourceType,            // 6
	UsersResourceType,                // 7
	VariablesResourceType,            // 8
	SecretsResourceType,              // 10
	DocumentsResourceType,            // 13
	NotificationRuleResourceType,     // 14
	NotificationEndpointResourceType, // 15
	ChecksResourceType,               // 16
	DBRPResourceType,                 // 17
}

// Valid checks if the resource type is a member of the ResourceType enum.
func (r Resource) Valid() (err error) {
	return r.Type.Valid()
}

// Valid checks if the resource type is a member of the ResourceType enum.
func (t ResourceType) Valid() (err error) {
	switch t {
	case AuthorizationsResourceType: // 0
	case BucketsResourceType: // 1
	case DashboardsResourceType: // 2
	case OrgsResourceType: // 3
	case TasksResourceType: // 4
	case TelegrafsResourceType: // 5
	case SourcesResourceType: // 6
	case UsersResourceType: // 7
	case VariablesResourceType: // 8
	case ScraperResourceType: // 9
	case SecretsResourceType: // 10
	case LabelsResourceType: // 11
	case ViewsResourceType: // 12
	case DocumentsResourceType: // 13
	case NotificationRuleResourceType: // 14
	case NotificationEndpointResourceType: // 15
	case ChecksResourceType: // 16
	case DBRPResourceType: // 17
	default:
		err = ErrInvalidResourceType
	}

	return err
}

type PermissionSet []Permission

func (ps PermissionSet) Allowed(p Permission) bool {
	return PermissionAllowed(p, ps)
}

// Permission defines an action and a resource.
type Permission struct {
	Action   Action   `json:"action"`
	Resource Resource `json:"resource"`
}

// Matches returns whether or not one permission matches the other.
func (p Permission) Matches(perm Permission) bool {
	if _, set := os.LookupEnv("MATCHER_BEHAVIOR"); set {
		return p.matchesV2(perm)
	}
	return p.matchesV1(perm)
}

func (p Permission) matchesV1(perm Permission) bool {
	if p.Action != perm.Action {
		return false
	}

	if p.Resource.Type != perm.Resource.Type {
		return false
	}

	if p.Resource.OrgID == nil && p.Resource.ID == nil {
		return true
	}

	if p.Resource.OrgID != nil && perm.Resource.OrgID != nil && p.Resource.ID != nil && perm.Resource.ID != nil {
		if *p.Resource.OrgID != *perm.Resource.OrgID && *p.Resource.ID == *perm.Resource.ID {
			fmt.Printf("Old match used: p.Resource.OrgID=%s perm.Resource.OrgID=%s p.Resource.ID=%s",
				*p.Resource.OrgID, *perm.Resource.OrgID, *p.Resource.ID)
		}
	}

	if p.Resource.OrgID != nil && p.Resource.ID == nil {
		pOrgID := *p.Resource.OrgID
		if perm.Resource.OrgID != nil {
			permOrgID := *perm.Resource.OrgID
			if pOrgID == permOrgID {
				return true
			}
		}
	}

	if p.Resource.ID != nil {
		pID := *p.Resource.ID
		if perm.Resource.ID != nil {
			permID := *perm.Resource.ID
			if pID == permID {
				return true
			}
		}
	}

	return false
}

func (p Permission) matchesV2(perm Permission) bool {
	if p.Action != perm.Action {
		return false
	}

	if p.Resource.Type != perm.Resource.Type {
		return false
	}

	if p.Resource.OrgID == nil && p.Resource.ID == nil {
		return true
	}

	if p.Resource.OrgID != nil {
		if perm.Resource.OrgID != nil {
			if *p.Resource.OrgID == *perm.Resource.OrgID {
				if p.Resource.ID == nil {
					return true
				}
				if perm.Resource.ID != nil {
					return *p.Resource.ID == *perm.Resource.ID
				}
			}
			return false
		}
	}

	if p.Resource.ID != nil {
		pID := *p.Resource.ID
		if perm.Resource.ID != nil {
			permID := *perm.Resource.ID
			if pID == permID {
				return true
			}
		}
	}

	return false
}

func (p Permission) String() string {
	return fmt.Sprintf("%s:%s", p.Action, p.Resource)
}

// Valid checks if there the resource and action provided is known.
func (p *Permission) Valid() error {
	if err := p.Resource.Valid(); err != nil {
		return &Error{
			Code: EInvalid,
			Err:  err,
			Msg:  "invalid resource type for permission",
		}
	}

	if err := p.Action.Valid(); err != nil {
		return &Error{
			Code: EInvalid,
			Err:  err,
			Msg:  "invalid action type for permission",
		}
	}

	if p.Resource.OrgID != nil && !p.Resource.OrgID.Valid() {
		return &Error{
			Code: EInvalid,
			Err:  ErrInvalidID,
			Msg:  "invalid org id for permission",
		}
	}

	if p.Resource.ID != nil && !p.Resource.ID.Valid() {
		return &Error{
			Code: EInvalid,
			Err:  ErrInvalidID,
			Msg:  "invalid id for permission",
		}
	}

	return nil
}

// NewPermission returns a permission with provided arguments.
func NewPermission(a Action, rt ResourceType, orgID ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type:  rt,
			OrgID: &orgID,
		},
	}

	return p, p.Valid()
}

// NewResourcePermission returns a permission with provided arguments.
func NewResourcePermission(a Action, rt ResourceType, rid ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type: rt,
			ID:   &rid,
		},
	}

	return p, p.Valid()
}

// NewGlobalPermission constructs a global permission capable of accessing any resource of type rt.
func NewGlobalPermission(a Action, rt ResourceType) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type: rt,
		},
	}
	return p, p.Valid()
}

// NewPermissionAtID creates a permission with the provided arguments.
func NewPermissionAtID(id ID, a Action, rt ResourceType, orgID ID) (*Permission, error) {
	p := &Permission{
		Action: a,
		Resource: Resource{
			Type:  rt,
			OrgID: &orgID,
			ID:    &id,
		},
	}

	return p, p.Valid()
}

// OperPermissions are the default permissions for those who setup the application.
func OperPermissions() []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		for _, a := range actions {
			ps = append(ps, Permission{Action: a, Resource: Resource{Type: r}})
		}
	}

	return ps
}

// ReadAllPermissions represents permission to read all data and metadata.
// Like OperPermissions, but allows read-only users.
func ReadAllPermissions() []Permission {
	ps := make([]Permission, len(AllResourceTypes))
	for i, t := range AllResourceTypes {
		ps[i] = Permission{Action: ReadAction, Resource: Resource{Type: t}}
	}
	return ps
}

// OwnerPermissions are the default permissions for those who own a resource.
func OwnerPermissions(orgID ID) []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		for _, a := range actions {
			if r == OrgsResourceType {
				ps = append(ps, Permission{Action: a, Resource: Resource{Type: r, ID: &orgID}})
				continue
			}
			ps = append(ps, Permission{Action: a, Resource: Resource{Type: r, OrgID: &orgID}})
		}
	}
	return ps
}

// MePermissions is the permission to read/write myself.
func MePermissions(userID ID) []Permission {
	ps := []Permission{}
	for _, a := range actions {
		ps = append(ps, Permission{Action: a, Resource: Resource{Type: UsersResourceType, ID: &userID}})
	}

	return ps
}

// MemberPermissions are the default permissions for those who can see a resource.
func MemberPermissions(orgID ID) []Permission {
	ps := []Permission{}
	for _, r := range AllResourceTypes {
		if r == OrgsResourceType {
			ps = append(ps, Permission{Action: ReadAction, Resource: Resource{Type: r, ID: &orgID}})
			continue
		}
		ps = append(ps, Permission{Action: ReadAction, Resource: Resource{Type: r, OrgID: &orgID}})
	}

	return ps
}

// MemberPermissions are the default permissions for those who can see a resource.
func MemberBucketPermission(bucketID ID) Permission {
	return Permission{Action: ReadAction, Resource: Resource{Type: BucketsResourceType, ID: &bucketID}}
}
<|endoftext|>