<commit_before>package gitref_test

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/hashicorp/terraform-exec/tfinstall"
	"github.com/hashicorp/terraform-exec/tfinstall/gitref"
)

// ensure the option satisfies the interface
var _ tfinstall.ExecPathFinder = &gitref.Option{}

func TestGitRef(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping git ref tests for short run")
	}

	cmd := exec.Command("go", "version")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("error with Go installation: %s\n%s", err, string(out))
	}
	t.Logf("go version\n%s", string(out))

	for n, c := range map[string]struct {
		expectedVersion string
		ref             string
	}{
		"branch v0.12": {"Terraform v0.12.", "refs/heads/v0.12"},
		"tag v0.12.29": {"Terraform v0.12.29", "refs/tags/v0.12.29"},
		// https://github.com/hashicorp/terraform/pull/25633
		"PR 25633": {"Terraform v0.12.29-dev", "refs/pull/25633/head"},
		//"commit 83630a7": {"Terraform v0.12.29", "83630a7003fb8b868a3bf940798326634c3c6acc"},
		"empty": {"Terraform v0.15.", ""}, // should pull master, which is currently 0.15 dev
	} {
		c := c
		t.Run(n, func(t *testing.T) {
			// these are really long running due to the compilation, run them in parallel
			t.Parallel()

			ctx := context.Background()

			// hacking this tmpdir to local dir due to circle perms, should be env var
			tmpDir, err := ioutil.TempDir("", "tfinstall-test")
			if err != nil {
				t.Fatal(err)
			}
			t.Cleanup(func() {
				os.RemoveAll(tmpDir)
			})

			t.Logf("finding / building ref %q...", c.ref)
			tfpath, err := tfinstall.Find(ctx, gitref.Install(c.ref, "", tmpDir))
			if err != nil {
				t.Fatalf("%T %s", err, err)
			}

			t.Logf("testing version cmd...")
			cmd := exec.Command(tfpath, "version")

			out, err := cmd.Output()
			if err != nil {
				t.Fatalf("%s\n\n%s", err, out)
			}

			actual := string(out)
			if !strings.Contains(actual, c.expectedVersion) {
				t.Fatalf("ran terraform version, expected %s, but got %s", c.expectedVersion, actual)
			}
		})
	}
}
<commit_msg>remove flaky git ref PR test case<commit_after>package gitref_test

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/hashicorp/terraform-exec/tfinstall"
	"github.com/hashicorp/terraform-exec/tfinstall/gitref"
)

// ensure the option satisfies the interface
var _ tfinstall.ExecPathFinder = &gitref.Option{}

func TestGitRef(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping git ref tests for short run")
	}

	cmd := exec.Command("go", "version")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("error with Go installation: %s\n%s", err, string(out))
	}
	t.Logf("go version\n%s", string(out))

	for n, c := range map[string]struct {
		expectedVersion string
		ref             string
	}{
		"branch v0.12": {"Terraform v0.12.", "refs/heads/v0.12"},
		"tag v0.12.29": {"Terraform v0.12.29", "refs/tags/v0.12.29"},
		// "commit 83630a7": {"Terraform v0.12.29", "83630a7003fb8b868a3bf940798326634c3c6acc"},
		"empty": {"Terraform v0.15.", ""}, // should pull master, which is currently 0.15 dev
	} {
		c := c
		t.Run(n, func(t *testing.T) {
			// these are really long running due to the compilation, run them in parallel
			t.Parallel()

			ctx := context.Background()

			// hacking this tmpdir to local dir due to circle perms, should be env var
			tmpDir, err := ioutil.TempDir("", "tfinstall-test")
			if err != nil {
				t.Fatal(err)
			}
			t.Cleanup(func() {
				os.RemoveAll(tmpDir)
			})

			t.Logf("finding / building ref %q...", c.ref)
			tfpath, err := tfinstall.Find(ctx, gitref.Install(c.ref, "", tmpDir))
			if err != nil {
				t.Fatalf("%T %s", err, err)
			}

			t.Logf("testing version cmd...")
			cmd := exec.Command(tfpath, "version")

			out, err := cmd.Output()
			if err != nil {
				t.Fatalf("%s\n\n%s", err, out)
			}

			actual := string(out)
			if !strings.Contains(actual, c.expectedVersion) {
				t.Fatalf("ran terraform version, expected %s, but got %s", c.expectedVersion, actual)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Code generated by goderive DO NOT EDIT.

package geom

// deriveCloneBounds returns a clone of the src parameter.
func deriveCloneBounds(src *Bounds) *Bounds {
	if src == nil {
		return nil
	}
	dst := new(Bounds)
	deriveDeepCopy(dst, src)
	return dst
}

// deriveCloneCoord returns a clone of the src parameter.
func deriveCloneCoord(src Coord) Coord {
	if src == nil {
		return nil
	}
	dst := make(Coord, len(src))
	deriveDeepCopy_(dst, src)
	return dst
}

// deriveCloneLinearRing returns a clone of the src parameter.
func deriveCloneLinearRing(src *LinearRing) *LinearRing {
	if src == nil {
		return nil
	}
	dst := new(LinearRing)
	deriveDeepCopy_1(dst, src)
	return dst
}

// deriveCloneLineString returns a clone of the src parameter.
func deriveCloneLineString(src *LineString) *LineString {
	if src == nil {
		return nil
	}
	dst := new(LineString)
	deriveDeepCopy_2(dst, src)
	return dst
}

// deriveCloneMultiLineString returns a clone of the src parameter.
func deriveCloneMultiLineString(src *MultiLineString) *MultiLineString {
	if src == nil {
		return nil
	}
	dst := new(MultiLineString)
	deriveDeepCopy_3(dst, src)
	return dst
}

// deriveCloneMultiPoint returns a clone of the src parameter.
func deriveCloneMultiPoint(src *MultiPoint) *MultiPoint {
	if src == nil {
		return nil
	}
	dst := new(MultiPoint)
	deriveDeepCopy_4(dst, src)
	return dst
}

// deriveCloneMultiPolygon returns a clone of the src parameter.
func deriveCloneMultiPolygon(src *MultiPolygon) *MultiPolygon {
	if src == nil {
		return nil
	}
	dst := new(MultiPolygon)
	deriveDeepCopy_5(dst, src)
	return dst
}

// deriveClonePoint returns a clone of the src parameter.
func deriveClonePoint(src *Point) *Point {
	if src == nil {
		return nil
	}
	dst := new(Point)
	deriveDeepCopy_6(dst, src)
	return dst
}

// deriveClonePolygon returns a clone of the src parameter.
func deriveClonePolygon(src *Polygon) *Polygon {
	if src == nil {
		return nil
	}
	dst := new(Polygon)
	deriveDeepCopy_7(dst, src)
	return dst
}

// deriveDeepCopy recursively copies the contents of src into dst.
func deriveDeepCopy(dst, src *Bounds) {
	dst.layout = src.layout
	if src.min == nil {
		dst.min = nil
	} else {
		if dst.min != nil {
			if len(src.min) > len(dst.min) {
				if cap(dst.min) >= len(src.min) {
					dst.min = (dst.min)[:len(src.min)]
				} else {
					dst.min = make([]float64, len(src.min))
				}
			} else if len(src.min) < len(dst.min) {
				dst.min = (dst.min)[:len(src.min)]
			}
		} else {
			dst.min = make([]float64, len(src.min))
		}
		copy(dst.min, src.min)
	}
	if src.max == nil {
		dst.max = nil
	} else {
		if dst.max != nil {
			if len(src.max) > len(dst.max) {
				if cap(dst.max) >= len(src.max) {
					dst.max = (dst.max)[:len(src.max)]
				} else {
					dst.max = make([]float64, len(src.max))
				}
			} else if len(src.max) < len(dst.max) {
				dst.max = (dst.max)[:len(src.max)]
			}
		} else {
			dst.max = make([]float64, len(src.max))
		}
		copy(dst.max, src.max)
	}
}

// deriveDeepCopy_ recursively copies the contents of src into dst.
func deriveDeepCopy_(dst, src Coord) {
	copy(dst, src)
}

// deriveDeepCopy_1 recursively copies the contents of src into dst.
func deriveDeepCopy_1(dst, src *LinearRing) {
	field := new(geom1)
	deriveDeepCopy_8(field, &src.geom1)
	dst.geom1 = *field
}

// deriveDeepCopy_2 recursively copies the contents of src into dst.
func deriveDeepCopy_2(dst, src *LineString) {
	field := new(geom1)
	deriveDeepCopy_8(field, &src.geom1)
	dst.geom1 = *field
}

// deriveDeepCopy_3 recursively copies the contents of src into dst.
func deriveDeepCopy_3(dst, src *MultiLineString) {
	field := new(geom2)
	deriveDeepCopy_9(field, &src.geom2)
	dst.geom2 = *field
}

// deriveDeepCopy_4 recursively copies the contents of src into dst.
func deriveDeepCopy_4(dst, src *MultiPoint) {
	field := new(geom1)
	deriveDeepCopy_8(field, &src.geom1)
	dst.geom1 = *field
}

// deriveDeepCopy_5 recursively copies the contents of src into dst.
func deriveDeepCopy_5(dst, src *MultiPolygon) {
	field := new(geom3)
	deriveDeepCopy_10(field, &src.geom3)
	dst.geom3 = *field
}

// deriveDeepCopy_6 recursively copies the contents of src into dst.
func deriveDeepCopy_6(dst, src *Point) {
	field := new(geom0)
	deriveDeepCopy_11(field, &src.geom0)
	dst.geom0 = *field
}

// deriveDeepCopy_7 recursively copies the contents of src into dst.
func deriveDeepCopy_7(dst, src *Polygon) {
	field := new(geom2)
	deriveDeepCopy_9(field, &src.geom2)
	dst.geom2 = *field
}

// deriveDeepCopy_8 recursively copies the contents of src into dst.
func deriveDeepCopy_8(dst, src *geom1) {
	field := new(geom0)
	deriveDeepCopy_11(field, &src.geom0)
	dst.geom0 = *field
}

// deriveDeepCopy_9 recursively copies the contents of src into dst.
func deriveDeepCopy_9(dst, src *geom2) {
	field := new(geom1)
	deriveDeepCopy_8(field, &src.geom1)
	dst.geom1 = *field
	if src.ends == nil {
		dst.ends = nil
	} else {
		if dst.ends != nil {
			if len(src.ends) > len(dst.ends) {
				if cap(dst.ends) >= len(src.ends) {
					dst.ends = (dst.ends)[:len(src.ends)]
				} else {
					dst.ends = make([]int, len(src.ends))
				}
			} else if len(src.ends) < len(dst.ends) {
				dst.ends = (dst.ends)[:len(src.ends)]
			}
		} else {
			dst.ends = make([]int, len(src.ends))
		}
		copy(dst.ends, src.ends)
	}
}

// deriveDeepCopy_10 recursively copies the contents of src into dst.
func deriveDeepCopy_10(dst, src *geom3) {
	field := new(geom1)
	deriveDeepCopy_8(field, &src.geom1)
	dst.geom1 = *field
	if src.endss == nil {
		dst.endss = nil
	} else {
		if dst.endss != nil {
			if len(src.endss) > len(dst.endss) {
				if cap(dst.endss) >= len(src.endss) {
					dst.endss = (dst.endss)[:len(src.endss)]
				} else {
					dst.endss = make([][]int, len(src.endss))
				}
			} else if len(src.endss) < len(dst.endss) {
				dst.endss = (dst.endss)[:len(src.endss)]
			}
		} else {
			dst.endss = make([][]int, len(src.endss))
		}
		deriveDeepCopy_12(dst.endss, src.endss)
	}
}

// deriveDeepCopy_11 recursively copies the contents of src into dst.
func deriveDeepCopy_11(dst, src *geom0) {
	dst.layout = src.layout
	dst.stride = src.stride
	if src.flatCoords == nil {
		dst.flatCoords = nil
	} else {
		if dst.flatCoords != nil {
			if len(src.flatCoords) > len(dst.flatCoords) {
				if cap(dst.flatCoords) >= len(src.flatCoords) {
					dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)]
				} else {
					dst.flatCoords = make([]float64, len(src.flatCoords))
				}
			} else if len(src.flatCoords) < len(dst.flatCoords) {
				dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)]
			}
		} else {
			dst.flatCoords = make([]float64, len(src.flatCoords))
		}
		copy(dst.flatCoords, src.flatCoords)
	}
	dst.srid = src.srid
}

// deriveDeepCopy_12 recursively copies the contents of src into dst.
func deriveDeepCopy_12(dst, src [][]int) {
	for src_i, src_value := range src {
		if src_value == nil {
			dst[src_i] = nil
		} else {
			if dst[src_i] != nil {
				if len(src_value) > len(dst[src_i]) {
					if cap(dst[src_i]) >= len(src_value) {
						dst[src_i] = (dst[src_i])[:len(src_value)]
					} else {
						dst[src_i] = make([]int, len(src_value))
					}
				} else if len(src_value) < len(dst[src_i]) {
					dst[src_i] = (dst[src_i])[:len(src_value)]
				}
			} else {
				dst[src_i] = make([]int, len(src_value))
			}
			copy(dst[src_i], src_value)
		}
	}
}
<commit_msg>Use latest goderive<commit_after>// Code generated by goderive DO NOT EDIT.

package geom

// deriveCloneBounds returns a clone of the src parameter.
func deriveCloneBounds(src *Bounds) *Bounds {
	if src == nil {
		return nil
	}
	dst := new(Bounds)
	deriveDeepCopy(dst, src)
	return dst
}

// deriveCloneCoord returns a clone of the src parameter.
func deriveCloneCoord(src Coord) Coord {
	if src == nil {
		return nil
	}
	dst := make(Coord, len(src))
	deriveDeepCopy_(dst, src)
	return dst
}

// deriveCloneLinearRing returns a clone of the src parameter.
func deriveCloneLinearRing(src *LinearRing) *LinearRing {
	if src == nil {
		return nil
	}
	dst := new(LinearRing)
	deriveDeepCopy_1(dst, src)
	return dst
}

// deriveCloneLineString returns a clone of the src parameter.
func deriveCloneLineString(src *LineString) *LineString {
	if src == nil {
		return nil
	}
	dst := new(LineString)
	deriveDeepCopy_2(dst, src)
	return dst
}

// deriveCloneMultiLineString returns a clone of the src parameter.
func deriveCloneMultiLineString(src *MultiLineString) *MultiLineString {
	if src == nil {
		return nil
	}
	dst := new(MultiLineString)
	deriveDeepCopy_3(dst, src)
	return dst
}

// deriveCloneMultiPoint returns a clone of the src parameter.
func deriveCloneMultiPoint(src *MultiPoint) *MultiPoint {
	if src == nil {
		return nil
	}
	dst := new(MultiPoint)
	deriveDeepCopy_4(dst, src)
	return dst
}

// deriveCloneMultiPolygon returns a clone of the src parameter.
func deriveCloneMultiPolygon(src *MultiPolygon) *MultiPolygon {
	if src == nil {
		return nil
	}
	dst := new(MultiPolygon)
	deriveDeepCopy_5(dst, src)
	return dst
}

// deriveClonePoint returns a clone of the src parameter.
func deriveClonePoint(src *Point) *Point {
	if src == nil {
		return nil
	}
	dst := new(Point)
	deriveDeepCopy_6(dst, src)
	return dst
}

// deriveClonePolygon returns a clone of the src parameter.
func deriveClonePolygon(src *Polygon) *Polygon {
	if src == nil {
		return nil
	}
	dst := new(Polygon)
	deriveDeepCopy_7(dst, src)
	return dst
}

// deriveDeepCopy recursively copies the contents of src into dst.
func deriveDeepCopy(dst, src *Bounds) {
	dst.layout = src.layout
	if src.min == nil {
		dst.min = nil
	} else {
		if dst.min != nil {
			if len(src.min) > len(dst.min) {
				if cap(dst.min) >= len(src.min) {
					dst.min = (dst.min)[:len(src.min)]
				} else {
					dst.min = make([]float64, len(src.min))
				}
			} else if len(src.min) < len(dst.min) {
				dst.min = (dst.min)[:len(src.min)]
			}
		} else {
			dst.min = make([]float64, len(src.min))
		}
		copy(dst.min, src.min)
	}
	if src.max == nil {
		dst.max = nil
	} else {
		if dst.max != nil {
			if len(src.max) > len(dst.max) {
				if cap(dst.max) >= len(src.max) {
					dst.max = (dst.max)[:len(src.max)]
				} else {
					dst.max = make([]float64, len(src.max))
				}
			} else if len(src.max) < len(dst.max) {
				dst.max = (dst.max)[:len(src.max)]
			}
		} else {
			dst.max = make([]float64, len(src.max))
		}
		copy(dst.max, src.max)
	}
}

// deriveDeepCopy_ recursively copies the contents of src into dst.
func deriveDeepCopy_(dst, src Coord) {
	copy(dst, src)
}

// deriveDeepCopy_1 recursively copies the contents of src into dst.
func deriveDeepCopy_1(dst, src *LinearRing) {
	func() {
		field := new(geom1)
		deriveDeepCopy_8(field, &src.geom1)
		dst.geom1 = *field
	}()
}

// deriveDeepCopy_2 recursively copies the contents of src into dst.
func deriveDeepCopy_2(dst, src *LineString) {
	func() {
		field := new(geom1)
		deriveDeepCopy_8(field, &src.geom1)
		dst.geom1 = *field
	}()
}

// deriveDeepCopy_3 recursively copies the contents of src into dst.
func deriveDeepCopy_3(dst, src *MultiLineString) {
	func() {
		field := new(geom2)
		deriveDeepCopy_9(field, &src.geom2)
		dst.geom2 = *field
	}()
}

// deriveDeepCopy_4 recursively copies the contents of src into dst.
func deriveDeepCopy_4(dst, src *MultiPoint) {
	func() {
		field := new(geom1)
		deriveDeepCopy_8(field, &src.geom1)
		dst.geom1 = *field
	}()
}

// deriveDeepCopy_5 recursively copies the contents of src into dst.
func deriveDeepCopy_5(dst, src *MultiPolygon) {
	func() {
		field := new(geom3)
		deriveDeepCopy_10(field, &src.geom3)
		dst.geom3 = *field
	}()
}

// deriveDeepCopy_6 recursively copies the contents of src into dst.
func deriveDeepCopy_6(dst, src *Point) {
	func() {
		field := new(geom0)
		deriveDeepCopy_11(field, &src.geom0)
		dst.geom0 = *field
	}()
}

// deriveDeepCopy_7 recursively copies the contents of src into dst.
func deriveDeepCopy_7(dst, src *Polygon) {
	func() {
		field := new(geom2)
		deriveDeepCopy_9(field, &src.geom2)
		dst.geom2 = *field
	}()
}

// deriveDeepCopy_8 recursively copies the contents of src into dst.
func deriveDeepCopy_8(dst, src *geom1) {
	func() {
		field := new(geom0)
		deriveDeepCopy_11(field, &src.geom0)
		dst.geom0 = *field
	}()
}

// deriveDeepCopy_9 recursively copies the contents of src into dst.
func deriveDeepCopy_9(dst, src *geom2) {
	func() {
		field := new(geom1)
		deriveDeepCopy_8(field, &src.geom1)
		dst.geom1 = *field
	}()
	if src.ends == nil {
		dst.ends = nil
	} else {
		if dst.ends != nil {
			if len(src.ends) > len(dst.ends) {
				if cap(dst.ends) >= len(src.ends) {
					dst.ends = (dst.ends)[:len(src.ends)]
				} else {
					dst.ends = make([]int, len(src.ends))
				}
			} else if len(src.ends) < len(dst.ends) {
				dst.ends = (dst.ends)[:len(src.ends)]
			}
		} else {
			dst.ends = make([]int, len(src.ends))
		}
		copy(dst.ends, src.ends)
	}
}

// deriveDeepCopy_10 recursively copies the contents of src into dst.
func deriveDeepCopy_10(dst, src *geom3) {
	func() {
		field := new(geom1)
		deriveDeepCopy_8(field, &src.geom1)
		dst.geom1 = *field
	}()
	if src.endss == nil {
		dst.endss = nil
	} else {
		if dst.endss != nil {
			if len(src.endss) > len(dst.endss) {
				if cap(dst.endss) >= len(src.endss) {
					dst.endss = (dst.endss)[:len(src.endss)]
				} else {
					dst.endss = make([][]int, len(src.endss))
				}
			} else if len(src.endss) < len(dst.endss) {
				dst.endss = (dst.endss)[:len(src.endss)]
			}
		} else {
			dst.endss = make([][]int, len(src.endss))
		}
		deriveDeepCopy_12(dst.endss, src.endss)
	}
}

// deriveDeepCopy_11 recursively copies the contents of src into dst.
func deriveDeepCopy_11(dst, src *geom0) {
	dst.layout = src.layout
	dst.stride = src.stride
	if src.flatCoords == nil {
		dst.flatCoords = nil
	} else {
		if dst.flatCoords != nil {
			if len(src.flatCoords) > len(dst.flatCoords) {
				if cap(dst.flatCoords) >= len(src.flatCoords) {
					dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)]
				} else {
					dst.flatCoords = make([]float64, len(src.flatCoords))
				}
			} else if len(src.flatCoords) < len(dst.flatCoords) {
				dst.flatCoords = (dst.flatCoords)[:len(src.flatCoords)]
			}
		} else {
			dst.flatCoords = make([]float64, len(src.flatCoords))
		}
		copy(dst.flatCoords, src.flatCoords)
	}
	dst.srid = src.srid
}

// deriveDeepCopy_12 recursively copies the contents of src into dst.
func deriveDeepCopy_12(dst, src [][]int) {
	for src_i, src_value := range src {
		if src_value == nil {
			dst[src_i] = nil
		} else {
			if dst[src_i] != nil {
				if len(src_value) > len(dst[src_i]) {
					if cap(dst[src_i]) >= len(src_value) {
						dst[src_i] = (dst[src_i])[:len(src_value)]
					} else {
						dst[src_i] = make([]int, len(src_value))
					}
				} else if len(src_value) < len(dst[src_i]) {
					dst[src_i] = (dst[src_i])[:len(src_value)]
				}
			} else {
				dst[src_i] = make([]int, len(src_value))
			}
			copy(dst[src_i], src_value)
		}
	}
}
<|endoftext|>


<commit_before>package rabbithole

import (
	"encoding/json"
	"net/http"
	"net/url"
)

// Information about backing queue (queue storage engine).
type BackingQueueStatus struct {
	Q1                    int     `json:"q1"`
	Q2                    int     `json:"q2"`
	Q3                    int     `json:"q3"`
	Q4                    int     `json:"q4"`
	// Total queue length
	Length                int64   `json:"len"`
	// Number of pending acks from consumers
	PendingAcks           int64   `json:"pending_acks"`
	// Number of messages held in RAM
	RAMMessageCount       int64   `json:"ram_msg_count"`
	// Number of outstanding acks held in RAM
	RAMAckCount           int64   `json:"ram_ack_count"`
	// Number of persistent messages in the store
	PersistentCount       int64   `json:"persistent_count"`
	// Average ingress (inbound) rate, not including messages
	// that straight through to auto-acking consumers.
	AverageIngressRate    float64 `json:"avg_ingress_rate"`
	// Average egress (outbound) rate, not including messages
	// that straight through to auto-acking consumers.
	AverageEgressRate     float64 `json:"avg_egress_rate"`
	// Average ingress rate for acknowledgements (from consumers)
	AverageAckIngressRate float32 `json:"avg_ack_ingress_rate"`
	// Average egress rate for acknowledgements (via publisher confirms)
	AverageAckEgressRate  float32 `json:"avg_ack_egress_rate"`
}

type OwnerPidDetails struct {
	Name     string `json:"name"`
	PeerPort Port   `json:"peer_port"`
	PeerHost string `json:"peer_host"`
}

type QueueInfo struct {
	// Queue name
	Name       string                 `json:"name"`
	// Virtual host this queue belongs to
	Vhost      string                 `json:"vhost"`
	// Is this queue durable?
	Durable    bool                   `json:"durable"`
	// Is this queue auto-delted?
	AutoDelete bool                   `json:"auto_delete"`
	// Extra queue arguments
	Arguments  map[string]interface{} `json:"arguments"`

	// RabbitMQ node that hosts master for this queue
	Node   string `json:"node"`
	// Queue status
	Status string `json:"status"`

	// Total amount of RAM used by this queue
	Memory               int64  `json:"memory"`
	// How many consumers this queue has
	Consumers            int    `json:"consumers"`
	// If there is an exclusive consumer, its consumer tag
	ExclusiveConsumerTag string `json:"exclusive_consumer_tag"`

	// Policy applied to this queue, if any
	Policy string `json:"policy"`

	// Total number of messages in this queue
	Messages        int         `json:"messages"`
	MessagesDetails RateDetails `json:"messages_details"`

	// Number of messages ready to be delivered
	MessagesReady        int         `json:"messages_ready"`
	MessagesReadyDetails RateDetails `json:"messages_ready_details"`

	// Number of messages delivered and pending acknowledgements from consumers
	MessagesUnacknowledged        int         `json:"messages_unacknowledged"`
	MessagesUnacknowledgedDetails RateDetails `json:"messages_unacknowledged_details"`

	MessageStats MessageStats `json:"message_stats"`

	OwnerPidDetails OwnerPidDetails `json:"owner_pid_details"`

	BackingQueueStatus BackingQueueStatus `json:"backing_queue_status"`
}

type DetailedQueueInfo QueueInfo

//
// GET /api/queues
//

// [
//   {
//     "owner_pid_details": {
//       "name": "127.0.0.1:46928 -> 127.0.0.1:5672",
//       "peer_port": 46928,
//       "peer_host": "127.0.0.1"
//     },
//     "message_stats": {
//       "publish": 19830,
//       "publish_details": {
//         "rate": 5
//       }
//     },
//     "messages": 15,
//     "messages_details": {
//       "rate": 0
//     },
//     "messages_ready": 15,
//     "messages_ready_details": {
//       "rate": 0
//     },
//     "messages_unacknowledged": 0,
//     "messages_unacknowledged_details": {
//       "rate": 0
//     },
//     "policy": "",
//     "exclusive_consumer_tag": "",
//     "consumers": 0,
//     "memory": 143112,
//     "backing_queue_status": {
//       "q1": 0,
//       "q2": 0,
//       "delta": [
//         "delta",
//         "undefined",
//         0,
//         "undefined"
//       ],
//       "q3": 0,
//       "q4": 15,
//       "len": 15,
//       "pending_acks": 0,
//       "target_ram_count": "infinity",
//       "ram_msg_count": 15,
//       "ram_ack_count": 0,
//       "next_seq_id": 19830,
//       "persistent_count": 0,
//       "avg_ingress_rate": 4.9920127795527,
//       "avg_egress_rate": 4.9920127795527,
//       "avg_ack_ingress_rate": 0,
//       "avg_ack_egress_rate": 0
//     },
//     "status": "running",
//     "name": "amq.gen-QLEaT5Rn_ogbN3O8ZOQt3Q",
//     "vhost": "rabbit\/hole",
//     "durable": false,
//     "auto_delete": false,
//     "arguments": {
//       "x-message-ttl": 5000
//     },
//     "node": "rabbit@marzo"
//   }
// ]

func (c *Client) ListQueues() (rec []QueueInfo, err error) {
	req, err := newGETRequest(c, "queues")
	if err != nil {
		return []QueueInfo{}, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return []QueueInfo{}, err
	}

	return rec, nil
}

//
// GET /api/queues/{vhost}
//

func (c *Client) ListQueuesIn(vhost string) (rec []QueueInfo, err error) {
	req, err := newGETRequest(c, "queues/"+url.QueryEscape(vhost))
	if err != nil {
		return []QueueInfo{}, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return []QueueInfo{}, err
	}

	return rec, nil
}

//
// GET /api/queues/{vhost}/{name}
//

func (c *Client) GetQueue(vhost, queue string) (rec *DetailedQueueInfo, err error) {
	req, err := newGETRequest(c, "queues/"+url.QueryEscape(vhost)+"/"+queue)
	if err != nil {
		return nil, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return nil, err
	}

	return rec, nil
}

//
// PUT /api/exchanges/{vhost}/{exchange}
//

type QueueSettings struct {
	Durable    bool                   `json:"durable"`
	AutoDelete bool                   `json:"auto_delete"`
	Arguments  map[string]interface{} `json:"arguments"`
}

func (c *Client) DeclareQueue(vhost, queue string, info QueueSettings) (res *http.Response, err error) {
	if info.Arguments == nil {
		info.Arguments = make(map[string]interface{})
	}
	body, err := json.Marshal(info)
	if err != nil {
		return nil, err
	}

	req, err := newRequestWithBody(c, "PUT", "queues/"+url.QueryEscape(vhost)+"/"+url.QueryEscape(queue), body)
	if err != nil {
		return nil, err
	}

	res, err = executeRequest(c, req)
	if err != nil {
		return nil, err
	}

	return res, nil
}

//
// DELETE /api/queues/{vhost}/{name}
//

func (c *Client) DeleteQueue(vhost, queue string) (res *http.Response, err error) {
	req, err := newRequestWithBody(c, "DELETE", "queues/"+url.QueryEscape(vhost)+"/"+url.QueryEscape(queue), nil)
	if err != nil {
		return nil, err
	}

	res, err = executeRequest(c, req)
	if err != nil {
		return nil, err
	}

	return res, nil
}
<commit_msg>Correct backing queue field documentation<commit_after>package rabbithole

import (
	"encoding/json"
	"net/http"
	"net/url"
)

// Information about backing queue (queue storage engine).
type BackingQueueStatus struct {
	Q1                    int     `json:"q1"`
	Q2                    int     `json:"q2"`
	Q3                    int     `json:"q3"`
	Q4                    int     `json:"q4"`
	// Total queue length
	Length                int64   `json:"len"`
	// Number of pending acks from consumers
	PendingAcks           int64   `json:"pending_acks"`
	// Number of messages held in RAM
	RAMMessageCount       int64   `json:"ram_msg_count"`
	// Number of outstanding acks held in RAM
	RAMAckCount           int64   `json:"ram_ack_count"`
	// Number of persistent messages in the store
	PersistentCount       int64   `json:"persistent_count"`
	// Average ingress (inbound) rate, not including messages
	// that straight through to auto-acking consumers.
	AverageIngressRate    float64 `json:"avg_ingress_rate"`
	// Average egress (outbound) rate, not including messages
	// that straight through to auto-acking consumers.
	AverageEgressRate     float64 `json:"avg_egress_rate"`
	// rate at which unacknowledged message records enter RAM,
	// e.g. because messages are delivered requiring acknowledgement
	AverageAckIngressRate float32 `json:"avg_ack_ingress_rate"`
	// rate at which unacknowledged message records leave RAM,
	// e.g. because acks arrive or unacked messages are paged out
	AverageAckEgressRate  float32 `json:"avg_ack_egress_rate"`
}

type OwnerPidDetails struct {
	Name     string `json:"name"`
	PeerPort Port   `json:"peer_port"`
	PeerHost string `json:"peer_host"`
}

type QueueInfo struct {
	// Queue name
	Name       string                 `json:"name"`
	// Virtual host this queue belongs to
	Vhost      string                 `json:"vhost"`
	// Is this queue durable?
	Durable    bool                   `json:"durable"`
	// Is this queue auto-delted?
	AutoDelete bool                   `json:"auto_delete"`
	// Extra queue arguments
	Arguments  map[string]interface{} `json:"arguments"`

	// RabbitMQ node that hosts master for this queue
	Node   string `json:"node"`
	// Queue status
	Status string `json:"status"`

	// Total amount of RAM used by this queue
	Memory               int64  `json:"memory"`
	// How many consumers this queue has
	Consumers            int    `json:"consumers"`
	// If there is an exclusive consumer, its consumer tag
	ExclusiveConsumerTag string `json:"exclusive_consumer_tag"`

	// Policy applied to this queue, if any
	Policy string `json:"policy"`

	// Total number of messages in this queue
	Messages        int         `json:"messages"`
	MessagesDetails RateDetails `json:"messages_details"`

	// Number of messages ready to be delivered
	MessagesReady        int         `json:"messages_ready"`
	MessagesReadyDetails RateDetails `json:"messages_ready_details"`

	// Number of messages delivered and pending acknowledgements from consumers
	MessagesUnacknowledged        int         `json:"messages_unacknowledged"`
	MessagesUnacknowledgedDetails RateDetails `json:"messages_unacknowledged_details"`

	MessageStats MessageStats `json:"message_stats"`

	OwnerPidDetails OwnerPidDetails `json:"owner_pid_details"`

	BackingQueueStatus BackingQueueStatus `json:"backing_queue_status"`
}

type DetailedQueueInfo QueueInfo

//
// GET /api/queues
//

// [
//   {
//     "owner_pid_details": {
//       "name": "127.0.0.1:46928 -> 127.0.0.1:5672",
//       "peer_port": 46928,
//       "peer_host": "127.0.0.1"
//     },
//     "message_stats": {
//       "publish": 19830,
//       "publish_details": {
//         "rate": 5
//       }
//     },
//     "messages": 15,
//     "messages_details": {
//       "rate": 0
//     },
//     "messages_ready": 15,
//     "messages_ready_details": {
//       "rate": 0
//     },
//     "messages_unacknowledged": 0,
//     "messages_unacknowledged_details": {
//       "rate": 0
//     },
//     "policy": "",
//     "exclusive_consumer_tag": "",
//     "consumers": 0,
//     "memory": 143112,
//     "backing_queue_status": {
//       "q1": 0,
//       "q2": 0,
//       "delta": [
//         "delta",
//         "undefined",
//         0,
//         "undefined"
//       ],
//       "q3": 0,
//       "q4": 15,
//       "len": 15,
//       "pending_acks": 0,
//       "target_ram_count": "infinity",
//       "ram_msg_count": 15,
//       "ram_ack_count": 0,
//       "next_seq_id": 19830,
//       "persistent_count": 0,
//       "avg_ingress_rate": 4.9920127795527,
//       "avg_egress_rate": 4.9920127795527,
//       "avg_ack_ingress_rate": 0,
//       "avg_ack_egress_rate": 0
//     },
//     "status": "running",
//     "name": "amq.gen-QLEaT5Rn_ogbN3O8ZOQt3Q",
//     "vhost": "rabbit\/hole",
//     "durable": false,
//     "auto_delete": false,
//     "arguments": {
//       "x-message-ttl": 5000
//     },
//     "node": "rabbit@marzo"
//   }
// ]

func (c *Client) ListQueues() (rec []QueueInfo, err error) {
	req, err := newGETRequest(c, "queues")
	if err != nil {
		return []QueueInfo{}, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return []QueueInfo{}, err
	}

	return rec, nil
}

//
// GET /api/queues/{vhost}
//

func (c *Client) ListQueuesIn(vhost string) (rec []QueueInfo, err error) {
	req, err := newGETRequest(c, "queues/"+url.QueryEscape(vhost))
	if err != nil {
		return []QueueInfo{}, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return []QueueInfo{}, err
	}

	return rec, nil
}

//
// GET /api/queues/{vhost}/{name}
//

func (c *Client) GetQueue(vhost, queue string) (rec *DetailedQueueInfo, err error) {
	req, err := newGETRequest(c, "queues/"+url.QueryEscape(vhost)+"/"+queue)
	if err != nil {
		return nil, err
	}

	if err = executeAndParseRequest(req, &rec); err != nil {
		return nil, err
	}

	return rec, nil
}

//
// PUT /api/exchanges/{vhost}/{exchange}
//

type QueueSettings struct {
	Durable    bool                   `json:"durable"`
	AutoDelete bool                   `json:"auto_delete"`
	Arguments  map[string]interface{} `json:"arguments"`
}

func (c *Client) DeclareQueue(vhost, queue string, info QueueSettings) (res *http.Response, err error) {
	if info.Arguments == nil {
		info.Arguments = make(map[string]interface{})
	}
	body, err := json.Marshal(info)
	if err != nil {
		return nil, err
	}

	req, err := newRequestWithBody(c, "PUT", "queues/"+url.QueryEscape(vhost)+"/"+url.QueryEscape(queue), body)
	if err != nil {
		return nil, err
	}

	res, err = executeRequest(c, req)
	if err != nil {
		return nil, err
	}

	return res, nil
}

//
// DELETE /api/queues/{vhost}/{name}
//

func (c *Client) DeleteQueue(vhost, queue string) (res *http.Response, err error) {
	req, err := newRequestWithBody(c, "DELETE", "queues/"+url.QueryEscape(vhost)+"/"+url.QueryEscape(queue), nil)
	if err != nil {
		return nil, err
	}

	res, err = executeRequest(c, req)
	if err != nil {
		return nil, err
	}

	return res, nil
}
<|endoftext|>


<commit_before>package clock

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestSleep(t *testing.T) {
	start := Now()

	// When
	Sleep(100 * time.Millisecond)

	// Then
	if Now().Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestAfter(t *testing.T) {
	start := Now()

	// When
	end := <-After(100 * time.Millisecond)

	// Then
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestAfterFunc(t *testing.T) {
	start := Now()
	endCh := make(chan time.Time, 1)

	// When
	AfterFunc(100*time.Millisecond, func() { endCh <- time.Now() })

	// Then
	end := <-endCh
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestNewTimer(t *testing.T) {
	start := Now()

	// When
	timer := NewTimer(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestTimerStop(t *testing.T) {
	timer := NewTimer(50 * time.Millisecond)

	// When
	active := timer.Stop()

	// Then
	assert.Equal(t, true, active)
	time.Sleep(100)
	select {
	case <-timer.C():
		assert.Fail(t, "Timer should not have fired")
	default:
	}
}

func TestTimerReset(t *testing.T) {
	start := time.Now()
	timer := NewTimer(300 * time.Millisecond)

	// When
	timer.Reset(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) > 150*time.Millisecond {
		assert.Fail(t, "Waited too long")
	}
}

func TestNewTicker(t *testing.T) {
	start := Now()

	// When
	timer := NewTicker(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
	end = <-timer.C()
	if end.Sub(start) < 200*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}

	timer.Stop()
	time.Sleep(150)
	select {
	case <-timer.C():
		assert.Fail(t, "Ticker should not have fired")
	default:
	}
}

func TestTick(t *testing.T) {
	start := Now()

	// When
	ch := Tick(100 * time.Millisecond)

	// Then
	end := <-ch
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
	end = <-ch
	if end.Sub(start) < 200*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestNewStoppedTimer(t *testing.T) {
	timer := NewStoppedTimer()

	// When/Then
	select {
	case <-timer.C():
		assert.Fail(t, "Timer should not have fired")
	default:
	}
	assert.Equal(t, false, timer.Stop())
}
<commit_msg>chore: clock test on darwin<commit_after>package clock

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestSleep(t *testing.T) {
	start := Now()

	// When
	Sleep(100 * time.Millisecond)

	// Then
	if Now().Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestAfter(t *testing.T) {
	start := Now()

	// When
	end := <-After(100 * time.Millisecond)

	// Then
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestAfterFunc(t *testing.T) {
	start := Now()
	endCh := make(chan time.Time, 1)

	// When
	AfterFunc(100*time.Millisecond, func() { endCh <- time.Now() })

	// Then
	end := <-endCh
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestNewTimer(t *testing.T) {
	start := Now()

	// When
	timer := NewTimer(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestTimerStop(t *testing.T) {
	timer := NewTimer(50 * time.Millisecond)

	// When
	active := timer.Stop()

	// Then
	assert.Equal(t, true, active)
	time.Sleep(100)
	select {
	case <-timer.C():
		assert.Fail(t, "Timer should not have fired")
	default:
	}
}

func TestTimerReset(t *testing.T) {
	t.Skip("fail on the CI for darwin")
	start := time.Now()
	timer := NewTimer(300 * time.Millisecond)

	// When
	timer.Reset(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) >= 150*time.Millisecond {
		assert.Fail(t, "Waited too long", end.Sub(start).String())
	}
}

func TestNewTicker(t *testing.T) {
	start := Now()

	// When
	timer := NewTicker(100 * time.Millisecond)

	// Then
	end := <-timer.C()
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
	end = <-timer.C()
	if end.Sub(start) < 200*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}

	timer.Stop()
	time.Sleep(150)
	select {
	case <-timer.C():
		assert.Fail(t, "Ticker should not have fired")
	default:
	}
}

func TestTick(t *testing.T) {
	start := Now()

	// When
	ch := Tick(100 * time.Millisecond)

	// Then
	end := <-ch
	if end.Sub(start) < 100*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
	end = <-ch
	if end.Sub(start) < 200*time.Millisecond {
		assert.Fail(t, "Sleep did not last long enough")
	}
}

func TestNewStoppedTimer(t *testing.T) {
	timer := NewStoppedTimer()

	// When/Then
	select {
	case <-timer.C():
		assert.Fail(t, "Timer should not have fired")
	default:
	}
	assert.Equal(t, false, timer.Stop())
}
<|endoftext|>


<commit_before>/*

Package ocsp exposes OCSP signing functionality, much like the signer
package does for certificate signing.  It also provies a basic OCSP
responder stack for serving pre-signed OCSP responses.

*/
package ocsp

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"io/ioutil"
	"strconv"
	"strings"
	"time"

	cferr "github.com/cloudflare/cfssl/errors"
	"github.com/cloudflare/cfssl/helpers"
	"github.com/cloudflare/cfssl/log"
	"golang.org/x/crypto/ocsp"
)

// revocationReasonCodes is a map between string reason codes
// to integers as defined in RFC 5280
var revocationReasonCodes = map[string]int{
	"unspecified":          ocsp.Unspecified,
	"keycompromise":        ocsp.KeyCompromise,
	"cacompromise":         ocsp.CACompromise,
	"affiliationchanged":   ocsp.AffiliationChanged,
	"superseded":           ocsp.Superseded,
	"cessationofoperation": ocsp.CessationOfOperation,
	"certificatehold":      ocsp.CertificateHold,
	"removefromcrl":        ocsp.RemoveFromCRL,
	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
	"aacompromise":         ocsp.AACompromise,
}

// StatusCode is a map between string statuses sent by cli/api
// to ocsp int statuses
var StatusCode = map[string]int{
	"good":    ocsp.Good,
	"revoked": ocsp.Revoked,
	"unknown": ocsp.Unknown,
}

// SignRequest represents the desired contents of a
// specific OCSP response.
type SignRequest struct {
	Certificate *x509.Certificate
	Status      string
	Reason      int
	RevokedAt   time.Time
	Extensions  []pkix.Extension
	// IssuerHash is the hashing function used to hash the issuer subject and public key
	// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
	// and crypto.SHA512. If zero, the default is crypto.SHA1.
	IssuerHash crypto.Hash
	// If provided ThisUpdate will override the default usage of time.Now().Truncate(time.Hour)
	ThisUpdate *time.Time
	// If provided NextUpdate will override the default usage of ThisUpdate.Add(signerInterval)
	NextUpdate *time.Time
}

// Signer represents a general signer of OCSP responses.  It is
// responsible for populating all fields in the OCSP response that
// are not reflected in the SignRequest.
type Signer interface {
	Sign(req SignRequest) ([]byte, error)
}

// StandardSigner is the default concrete type of OCSP signer.
// It represents a single responder (represented by a key and certificate)
// speaking for a single issuer (certificate).  It is assumed that OCSP
// responses are issued at a regular interval, which is used to compute
// the nextUpdate value based on the current time.
type StandardSigner struct {
	issuer    *x509.Certificate
	responder *x509.Certificate
	key       crypto.Signer
	interval  time.Duration
}

// ReasonStringToCode tries to convert a reason string to an integer code
func ReasonStringToCode(reason string) (reasonCode int, err error) {
	// default to 0
	if reason == "" {
		return 0, nil
	}

	reasonCode, present := revocationReasonCodes[strings.ToLower(reason)]
	if !present {
		reasonCode, err = strconv.Atoi(reason)
		if err != nil {
			return
		}
		if reasonCode >= ocsp.AACompromise || reasonCode <= ocsp.Unspecified {
			return 0, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
		}
	}

	return
}

// NewSignerFromFile reads the issuer cert, the responder cert and the responder key
// from PEM files, and takes an interval in seconds
func NewSignerFromFile(issuerFile, responderFile, keyFile string, interval time.Duration) (Signer, error) {
	log.Debug("Loading issuer cert: ", issuerFile)
	issuerBytes, err := helpers.ReadBytes(issuerFile)
	if err != nil {
		return nil, err
	}
	log.Debug("Loading responder cert: ", responderFile)
	responderBytes, err := ioutil.ReadFile(responderFile)
	if err != nil {
		return nil, err
	}
	log.Debug("Loading responder key: ", keyFile)
	keyBytes, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
	}

	issuerCert, err := helpers.ParseCertificatePEM(issuerBytes)
	if err != nil {
		return nil, err
	}

	responderCert, err := helpers.ParseCertificatePEM(responderBytes)
	if err != nil {
		return nil, err
	}

	key, err := helpers.ParsePrivateKeyPEM(keyBytes)
	if err != nil {
		log.Debug("Malformed private key %v", err)
		return nil, err
	}

	return NewSigner(issuerCert, responderCert, key, interval)
}

// NewSigner simply constructs a new StandardSigner object from the inputs,
// taking the interval in seconds
func NewSigner(issuer, responder *x509.Certificate, key crypto.Signer, interval time.Duration) (Signer, error) {
	return &StandardSigner{
		issuer:    issuer,
		responder: responder,
		key:       key,
		interval:  interval,
	}, nil
}

// Sign is used with an OCSP signer to request the issuance of
// an OCSP response.
func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
	if req.Certificate == nil {
		return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed)
	}

	// Verify that req.Certificate is issued under s.issuer
	if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 {
		return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
	}

	err := req.Certificate.CheckSignatureFrom(s.issuer)
	if err != nil {
		return nil, cferr.New(cferr.CertificateError, cferr.VerifyFailed, err)
	}

	var thisUpdate, nextUpdate time.Time
	if req.ThisUpdate != nil {
		thisUpdate = *req.ThisUpdate
	} else {
		// Round thisUpdate times down to the nearest hour
		thisUpdate = time.Now().Truncate(time.Hour)
	}
	if req.NextUpdate != nil {
		nextUpdate = *req.NextUpdate
	} else {
		nextUpdate = thisUpdate.Add(s.interval)
	}

	status, ok := StatusCode[req.Status]
	if !ok {
		return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
	}

	// If the OCSP responder is the same as the issuer, there is no need to
	// include any certificate in the OCSP response, which decreases the byte size
	// of OCSP responses dramatically.
	certificate := s.responder
	if s.issuer == s.responder || bytes.Equal(s.issuer.Raw, s.responder.Raw) {
		certificate = nil
	}

	template := ocsp.Response{
		Status:          status,
		SerialNumber:    req.Certificate.SerialNumber,
		ThisUpdate:      thisUpdate,
		NextUpdate:      nextUpdate,
		Certificate:     certificate,
		ExtraExtensions: req.Extensions,
		IssuerHash:      req.IssuerHash,
	}

	if status == ocsp.Revoked {
		template.RevokedAt = req.RevokedAt
		template.RevocationReason = req.Reason
	}

	return ocsp.CreateResponse(s.issuer, s.responder, template, s.key)
}
<commit_msg>Update cferr from new to wrap<commit_after>/*

Package ocsp exposes OCSP signing functionality, much like the signer
package does for certificate signing.  It also provies a basic OCSP
responder stack for serving pre-signed OCSP responses.

*/
package ocsp

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"io/ioutil"
	"strconv"
	"strings"
	"time"

	cferr "github.com/cloudflare/cfssl/errors"
	"github.com/cloudflare/cfssl/helpers"
	"github.com/cloudflare/cfssl/log"
	"golang.org/x/crypto/ocsp"
)

// revocationReasonCodes is a map between string reason codes
// to integers as defined in RFC 5280
var revocationReasonCodes = map[string]int{
	"unspecified":          ocsp.Unspecified,
	"keycompromise":        ocsp.KeyCompromise,
	"cacompromise":         ocsp.CACompromise,
	"affiliationchanged":   ocsp.AffiliationChanged,
	"superseded":           ocsp.Superseded,
	"cessationofoperation": ocsp.CessationOfOperation,
	"certificatehold":      ocsp.CertificateHold,
	"removefromcrl":        ocsp.RemoveFromCRL,
	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
	"aacompromise":         ocsp.AACompromise,
}

// StatusCode is a map between string statuses sent by cli/api
// to ocsp int statuses
var StatusCode = map[string]int{
	"good":    ocsp.Good,
	"revoked": ocsp.Revoked,
	"unknown": ocsp.Unknown,
}

// SignRequest represents the desired contents of a
// specific OCSP response.
type SignRequest struct {
	Certificate *x509.Certificate
	Status      string
	Reason      int
	RevokedAt   time.Time
	Extensions  []pkix.Extension
	// IssuerHash is the hashing function used to hash the issuer subject and public key
	// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
	// and crypto.SHA512. If zero, the default is crypto.SHA1.
	IssuerHash crypto.Hash
	// If provided ThisUpdate will override the default usage of time.Now().Truncate(time.Hour)
	ThisUpdate *time.Time
	// If provided NextUpdate will override the default usage of ThisUpdate.Add(signerInterval)
	NextUpdate *time.Time
}

// Signer represents a general signer of OCSP responses.  It is
// responsible for populating all fields in the OCSP response that
// are not reflected in the SignRequest.
type Signer interface {
	Sign(req SignRequest) ([]byte, error)
}

// StandardSigner is the default concrete type of OCSP signer.
// It represents a single responder (represented by a key and certificate)
// speaking for a single issuer (certificate).  It is assumed that OCSP
// responses are issued at a regular interval, which is used to compute
// the nextUpdate value based on the current time.
type StandardSigner struct {
	issuer    *x509.Certificate
	responder *x509.Certificate
	key       crypto.Signer
	interval  time.Duration
}

// ReasonStringToCode tries to convert a reason string to an integer code
func ReasonStringToCode(reason string) (reasonCode int, err error) {
	// default to 0
	if reason == "" {
		return 0, nil
	}

	reasonCode, present := revocationReasonCodes[strings.ToLower(reason)]
	if !present {
		reasonCode, err = strconv.Atoi(reason)
		if err != nil {
			return
		}
		if reasonCode >= ocsp.AACompromise || reasonCode <= ocsp.Unspecified {
			return 0, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
		}
	}

	return
}

// NewSignerFromFile reads the issuer cert, the responder cert and the responder key
// from PEM files, and takes an interval in seconds
func NewSignerFromFile(issuerFile, responderFile, keyFile string, interval time.Duration) (Signer, error) {
	log.Debug("Loading issuer cert: ", issuerFile)
	issuerBytes, err := helpers.ReadBytes(issuerFile)
	if err != nil {
		return nil, err
	}
	log.Debug("Loading responder cert: ", responderFile)
	responderBytes, err := ioutil.ReadFile(responderFile)
	if err != nil {
		return nil, err
	}
	log.Debug("Loading responder key: ", keyFile)
	keyBytes, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
	}

	issuerCert, err := helpers.ParseCertificatePEM(issuerBytes)
	if err != nil {
		return nil, err
	}

	responderCert, err := helpers.ParseCertificatePEM(responderBytes)
	if err != nil {
		return nil, err
	}

	key, err := helpers.ParsePrivateKeyPEM(keyBytes)
	if err != nil {
		log.Debug("Malformed private key %v", err)
		return nil, err
	}

	return NewSigner(issuerCert, responderCert, key, interval)
}

// NewSigner simply constructs a new StandardSigner object from the inputs,
// taking the interval in seconds
func NewSigner(issuer, responder *x509.Certificate, key crypto.Signer, interval time.Duration) (Signer, error) {
	return &StandardSigner{
		issuer:    issuer,
		responder: responder,
		key:       key,
		interval:  interval,
	}, nil
}

// Sign is used with an OCSP signer to request the issuance of
// an OCSP response.
func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
	if req.Certificate == nil {
		return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed)
	}

	// Verify that req.Certificate is issued under s.issuer
	if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 {
		return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
	}

	err := req.Certificate.CheckSignatureFrom(s.issuer)
	if err != nil {
		return nil, cferr.Wrap(cferr.OCSPError, cferr.VerifyFailed, err)
	}

	var thisUpdate, nextUpdate time.Time
	if req.ThisUpdate != nil {
		thisUpdate = *req.ThisUpdate
	} else {
		// Round thisUpdate times down to the nearest hour
		thisUpdate = time.Now().Truncate(time.Hour)
	}
	if req.NextUpdate != nil {
		nextUpdate = *req.NextUpdate
	} else {
		nextUpdate = thisUpdate.Add(s.interval)
	}

	status, ok := StatusCode[req.Status]
	if !ok {
		return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
	}

	// If the OCSP responder is the same as the issuer, there is no need to
	// include any certificate in the OCSP response, which decreases the byte size
	// of OCSP responses dramatically.
	certificate := s.responder
	if s.issuer == s.responder || bytes.Equal(s.issuer.Raw, s.responder.Raw) {
		certificate = nil
	}

	template := ocsp.Response{
		Status:          status,
		SerialNumber:    req.Certificate.SerialNumber,
		ThisUpdate:      thisUpdate,
		NextUpdate:      nextUpdate,
		Certificate:     certificate,
		ExtraExtensions: req.Extensions,
		IssuerHash:      req.IssuerHash,
	}

	if status == ocsp.Revoked {
		template.RevokedAt = req.RevokedAt
		template.RevocationReason = req.Reason
	}

	return ocsp.CreateResponse(s.issuer, s.responder, template, s.key)
}
<|endoftext|>


<commit_before>//go:generate go tool yacc -o query.go -p cqbs query.y
package main

import (
	"flag"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gtfierro/cs262-project/common"
	"github.com/pkg/profile"
)

// config flags
var configfile = flag.String("c", "config.ini", "Path to configuration file")

func init() {
	// set up logging
	log.SetOutput(os.Stderr)
}

func main() {
	flag.Parse()
	// configure logging instance
	config, configLogmsg := common.LoadConfig(*configfile)
	common.SetupLogging(config)
	log.Info(configLogmsg)

	if config.Debug.Enable {
		var p interface {
			Stop()
		}
		switch config.Debug.ProfileType {
		case "cpu", "profile":
			p = profile.Start(profile.CPUProfile, profile.ProfilePath("."))
		case "block":
			p = profile.Start(profile.BlockProfile, profile.ProfilePath("."))
		case "mem":
			p = profile.Start(profile.MemProfile, profile.ProfilePath("."))
		}
		time.AfterFunc(time.Duration(config.Debug.ProfileLength)*time.Second, func() {
			p.Stop()
			os.Exit(0)
		})
	}

	server := NewServer(config)
	server.listenAndDispatch()
}
<commit_msg>add default case so we dont get a panic() on exit<commit_after>//go:generate go tool yacc -o query.go -p cqbs query.y
package main

import (
	"flag"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gtfierro/cs262-project/common"
	"github.com/pkg/profile"
)

// config flags
var configfile = flag.String("c", "config.ini", "Path to configuration file")

func init() {
	// set up logging
	log.SetOutput(os.Stderr)
}

func main() {
	flag.Parse()
	// configure logging instance
	config, configLogmsg := common.LoadConfig(*configfile)
	common.SetupLogging(config)
	log.Info(configLogmsg)

	if config.Debug.Enable {
		var p interface {
			Stop()
		}
		switch config.Debug.ProfileType {
		case "cpu", "profile":
			p = profile.Start(profile.CPUProfile, profile.ProfilePath("."))
		case "block":
			p = profile.Start(profile.BlockProfile, profile.ProfilePath("."))
		case "mem":
			p = profile.Start(profile.MemProfile, profile.ProfilePath("."))
		default:
			p = profile.Start(profile.CPUProfile, profile.ProfilePath("."))
		}
		time.AfterFunc(time.Duration(config.Debug.ProfileLength)*time.Second, func() {
			p.Stop()
			os.Exit(0)
		})
	}

	server := NewServer(config)
	server.listenAndDispatch()
}
<|endoftext|>


<commit_before>package siri

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/jbowtie/gokogiri/xml"
	"golang.org/x/text/encoding/charmap"
)

type Request interface {
	BuildXML() (string, error)
}

type SOAPClient struct {
	url string
}

func NewSOAPClient(url string) *SOAPClient {
	return &SOAPClient{url: url}
}
func (client *SOAPClient) URL() string {
	return client.url
}

func (client *SOAPClient) prepareAndSendRequest(request Request, resource string, acceptGzip bool) (xml.Node, error) {
	// Wrap the request XML
	soapEnvelope := NewSOAPEnvelopeBuffer()
	xml, err := request.BuildXML()
	if err != nil {
		return nil, err
	}

	soapEnvelope.WriteXML(xml)
	// Create http request
	httpRequest, err := http.NewRequest("POST", client.url, soapEnvelope)
	if err != nil {
		return nil, err
	}
	if acceptGzip {
		httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
	}
	httpRequest.Header.Set("Content-Type", "text/xml")
	httpRequest.ContentLength = soapEnvelope.Length()

	// Send http request
	response, err := http.DefaultClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Check response status
	if response.StatusCode != http.StatusOK {
		return nil, newSiriError(strings.Join([]string{"SIRI CRITICAL: HTTP status ", strconv.Itoa(response.StatusCode)}, ""))
	}

	if !strings.Contains(response.Header.Get("Content-Type"), "text/xml") {
		return nil, newSiriError(fmt.Sprintf("SIRI CRITICAL: HTTP Content-Type %v", response.Header.Get("Content-Type")))
	}

	// Check if response is gzip
	var responseReader io.Reader
	if acceptGzip && response.Header.Get("Content-Encoding") == "gzip" {
		gzipReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return nil, err
		}
		defer gzipReader.Close()
		responseReader = gzipReader
	} else {
		responseReader = response.Body
	}

	if response.Header.Get("Content-Type") == "text/xml;charset=ISO-8859-1" {
		responseReader = charmap.ISO8859_1.NewDecoder().Reader(responseReader)
	}

	// Create SOAPEnvelope and check body type
	envelope, err := NewSOAPEnvelope(responseReader)
	if err != nil {
		return nil, err
	}
	if envelope.BodyType() != resource {
		return nil, newSiriError(fmt.Sprintf("SIRI CRITICAL: Wrong Soap from server: %v", envelope.BodyType()))
	}

	return envelope.Body(), nil
}

func (client *SOAPClient) CheckStatus(request *SIRICheckStatusRequest) (*XMLCheckStatusResponse, error) {
	node, err := client.prepareAndSendRequest(request, "CheckStatusResponse", true)
	if err != nil {
		return nil, err
	}

	checkStatus := NewXMLCheckStatusResponse(node)
	return checkStatus, nil
}

func (client *SOAPClient) StopMonitoring(request *SIRIStopMonitoringRequest) (*XMLStopMonitoringResponse, error) {
	// WIP
	node, err := client.prepareAndSendRequest(request, "GetStopMonitoringResponse", false)
	if err != nil {
		return nil, err
	}

	stopMonitoring := NewXMLStopMonitoringResponse(node)
	return stopMonitoring, nil
}
<commit_msg>Refs #2966 ISO-8859-1 supporté 'proprement'<commit_after>package siri

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/jbowtie/gokogiri/xml"
	"golang.org/x/text/encoding/charmap"
)

type Request interface {
	BuildXML() (string, error)
}

type SOAPClient struct {
	url string
}

func NewSOAPClient(url string) *SOAPClient {
	return &SOAPClient{url: url}
}
func (client *SOAPClient) URL() string {
	return client.url
}

func (client *SOAPClient) responseFromFormat(body io.Reader, contentType string) io.Reader {
	r, _ := regexp.Compile("^text/xml;charset=([ -~]+)")
	s := r.FindStringSubmatch(contentType)
	if len(s) == 0 {
		return body
	}
	if s[1] == "ISO-8859-1" {
		return charmap.ISO8859_1.NewDecoder().Reader(body)
	}
	return body
}

func (client *SOAPClient) prepareAndSendRequest(request Request, resource string, acceptGzip bool) (xml.Node, error) {
	// Wrap the request XML
	soapEnvelope := NewSOAPEnvelopeBuffer()
	xml, err := request.BuildXML()
	if err != nil {
		return nil, err
	}

	soapEnvelope.WriteXML(xml)
	// Create http request
	httpRequest, err := http.NewRequest("POST", client.url, soapEnvelope)
	if err != nil {
		return nil, err
	}
	if acceptGzip {
		httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
	}
	httpRequest.Header.Set("Content-Type", "text/xml")
	httpRequest.ContentLength = soapEnvelope.Length()

	// Send http request
	response, err := http.DefaultClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Check response status
	if response.StatusCode != http.StatusOK {
		return nil, newSiriError(strings.Join([]string{"SIRI CRITICAL: HTTP status ", strconv.Itoa(response.StatusCode)}, ""))
	}

	if !strings.Contains(response.Header.Get("Content-Type"), "text/xml") {
		return nil, newSiriError(fmt.Sprintf("SIRI CRITICAL: HTTP Content-Type %v", response.Header.Get("Content-Type")))
	}

	// Check if response is gzip
	var responseReader io.Reader
	if acceptGzip && response.Header.Get("Content-Encoding") == "gzip" {
		gzipReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return nil, err
		}
		defer gzipReader.Close()
		responseReader = gzipReader
	} else {
		responseReader = client.responseFromFormat(response.Body, response.Header.Get("Content-Type"))
	}

	// Create SOAPEnvelope and check body type
	envelope, err := NewSOAPEnvelope(responseReader)
	if err != nil {
		return nil, err
	}
	if envelope.BodyType() != resource {
		return nil, newSiriError(fmt.Sprintf("SIRI CRITICAL: Wrong Soap from server: %v", envelope.BodyType()))
	}

	return envelope.Body(), nil
}

func (client *SOAPClient) CheckStatus(request *SIRICheckStatusRequest) (*XMLCheckStatusResponse, error) {
	node, err := client.prepareAndSendRequest(request, "CheckStatusResponse", true)
	if err != nil {
		return nil, err
	}

	checkStatus := NewXMLCheckStatusResponse(node)
	return checkStatus, nil
}

func (client *SOAPClient) StopMonitoring(request *SIRIStopMonitoringRequest) (*XMLStopMonitoringResponse, error) {
	// WIP
	node, err := client.prepareAndSendRequest(request, "GetStopMonitoringResponse", false)
	if err != nil {
		return nil, err
	}

	stopMonitoring := NewXMLStopMonitoringResponse(node)
	return stopMonitoring, nil
}
<|endoftext|>


<commit_before>package uploaders

import (
	"net/url"
	"os"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/secsy/goftp"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/types"
)

// FTPUpload uploades the file using FTP. Job Destination should be
// in format: ftp://login:password@host/path
func FTPUpload(logger lager.Logger, dbInstance db.Storage, jobID string) error {
	log := logger.Session("ftp-upload")
	log.Info("start", lager.Data{"job": jobID})
	defer log.Info("finished")

	job, err := dbInstance.RetrieveJob(jobID)
	if err != nil {
		log.Error("retrieving-job", err)
		return err
	}

	job.Status = types.JobUploading
	job, err = dbInstance.UpdateJob(job.ID, job)
	if err != nil {
		log.Error("updating-job", err)
		return err
	}

	u, err := url.Parse(job.Destination)
	if err != nil {
		return err
	}

	pw, isSet := u.User.Password()
	if !isSet {
		pw = ""
	}

	config := goftp.Config{
		User:               u.User.Username(),
		Password:           pw,
		ConnectionsPerHost: 10,
		Timeout:            10 * time.Second,
		Logger:             os.Stderr,
	}

	client, err := goftp.DialConfig(config, u.Host+":21")
	if err != nil {
		log.Error("dial-config-failed", err)
		return err
	}

	localFile, err := os.Open(job.LocalDestination)
	if err != nil {
		log.Error("opening-local-destination-failed", err)
		return err
	}

	err = client.Store("."+u.Path, localFile)
	if err != nil {
		log.Error("storing-file-failed", err)
		return err
	}

	return nil
}
<commit_msg>uploaders/ftp: support uploading HLS segments within a folder<commit_after>package uploaders

import (
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/secsy/goftp"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/types"
)

// FTPUpload uploades the file using FTP. Job Destination should be
// in format: ftp://login:password@host/path
func FTPUpload(logger lager.Logger, dbInstance db.Storage, jobID string) error {
	log := logger.Session("ftp-upload")
	log.Info("start", lager.Data{"job": jobID})
	defer log.Info("finished")

	job, err := dbInstance.RetrieveJob(jobID)
	if err != nil {
		log.Error("retrieving-job", err)
		return err
	}

	job.Status = types.JobUploading
	job, err = dbInstance.UpdateJob(job.ID, job)
	if err != nil {
		log.Error("updating-job", err)
		return err
	}

	u, err := url.Parse(job.Destination)
	if err != nil {
		return err
	}

	pw, isSet := u.User.Password()
	if !isSet {
		pw = ""
	}

	config := goftp.Config{
		User:               u.User.Username(),
		Password:           pw,
		ConnectionsPerHost: 10,
		Timeout:            10 * time.Second,
		Logger:             os.Stderr,
	}

	client, err := goftp.DialConfig(config, u.Host+":21")
	if err != nil {
		log.Error("dial-config-failed", err)
		return err
	}

	fileInfo, err := os.Stat(job.LocalDestination)
	if err != nil {
		log.Error("get-destination-info", err)
		return err
	}

	if fileInfo.IsDir() {
		base := path.Base(job.LocalDestination)
		client.Mkdir(base)
		files, err := ioutil.ReadDir(job.LocalDestination)
		if err != nil {
			log.Error("listing-files", err)
			return err
		}
		for _, file := range files {
			localFile, err := os.Open(job.LocalDestination + "/" + file.Name())
			defer localFile.Close()
			if err != nil {
				log.Error("opening-local-destination-failed", err)
				return err
			}
			client.Store("."+u.Path+"/"+file.Name(), localFile)

		}

	} else {
		localFile, err := os.Open(job.LocalDestination)
		defer localFile.Close()
		if err != nil {
			log.Error("opening-local-destination-failed", err)
			return err
		}

		err = client.Store("."+u.Path, localFile)
		if err != nil {
			log.Error("storing-file-failed", err)
			return err
		}
	}

	return err
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 tcsclient

import (
	"bytes"
	"errors"
	"net/http"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/ecs_client/authv4"
	"github.com/aws/amazon-ecs-agent/agent/ecs_client/authv4/credentials"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/aws/amazon-ecs-agent/agent/stats"
	"github.com/aws/amazon-ecs-agent/agent/tcs/model/ecstcs"
	"github.com/aws/amazon-ecs-agent/agent/wsclient"
	"github.com/gorilla/websocket"
)

// tasksInMessage is the maximum number of tasks that can be sent in a message to the backend
// This is a very conservative estimate assuming max allowed string lengths for all fields.
const tasksInMessage = 10

var log = logger.ForModule("tcs client")

// clientServer implements wsclient.ClientServer interface for metrics backend.
type clientServer struct {
	statsEngine            stats.Engine
	publishTicker          *time.Ticker
	publishMetricsInterval time.Duration
	wsclient.ClientServerImpl
	signer authv4.HttpSigner
}

// New returns a client/server to bidirectionally communicate with the backend.
// The returned struct should have both 'Connect' and 'Serve' called upon it
// before being used.
func New(url string, region string, credentialProvider credentials.AWSCredentialProvider, acceptInvalidCert bool, statsEngine stats.Engine, publishMetricsInterval time.Duration) wsclient.ClientServer {
	cs := &clientServer{
		statsEngine:            statsEngine,
		publishTicker:          nil,
		publishMetricsInterval: publishMetricsInterval,
		signer:                 authv4.NewHttpSigner(region, wsclient.ServiceName, credentialProvider, nil),
	}
	cs.URL = url
	cs.Region = region
	cs.CredentialProvider = credentialProvider
	cs.AcceptInvalidCert = acceptInvalidCert
	cs.ServiceError = &tcsError{}
	cs.RequestHandlers = make(map[string]wsclient.RequestHandler)
	cs.TypeDecoder = &TcsDecoder{}
	return cs
}

// Serve begins serving requests using previously registered handlers (see
// AddRequestHandler). All request handlers should be added prior to making this
// call as unhandled requests will be discarded.
func (cs *clientServer) Serve() error {
	log.Debug("Starting websocket poll loop")
	if cs.Conn == nil {
		return errors.New("nil connection")
	}

	// Start the timer function to publish metrics to the backend.
	if cs.statsEngine != nil {
		cs.publishTicker = time.NewTicker(cs.publishMetricsInterval)
		go cs.publishMetrics()
	}

	return cs.ConsumeMessages()
}

// MakeRequest makes a request using the given input. Note, the input *MUST* be
// a pointer to a valid backend type that this client recognises
func (cs *clientServer) MakeRequest(input interface{}) error {
	payload, err := cs.CreateRequestMessage(input)
	if err != nil {
		return err
	}

	log.Debug("sending payload", "payload", string(payload))
	data := cs.signRequest(payload)

	// Over the wire we send something like
	// {"type":"AckRequest","message":{"messageId":"xyz"}}
	return cs.Conn.WriteMessage(websocket.TextMessage, data)
}

func (cs *clientServer) signRequest(payload []byte) []byte {
	signer := authv4.NewHttpSigner(cs.Region, "ecs", cs.CredentialProvider, nil)
	reqBody := bytes.NewBuffer(payload)
	// NewRequest never returns an error if the url parses and we just verified
	// it did above
	request, _ := http.NewRequest("GET", cs.URL, reqBody)
	signer.SignHttpRequest(request)

	var data []byte
	for k, vs := range request.Header {
		for _, v := range vs {
			data = append(data, k...)
			data = append(data, ": "...)
			data = append(data, v...)
			data = append(data, "\r\n"...)
		}
	}
	data = append(data, "\r\n"...)
	data = append(data, payload...)

	return data
}

// Close closes the underlying connection.
func (cs *clientServer) Close() error {
	if cs.publishTicker != nil {
		cs.publishTicker.Stop()
	}
	if cs.Conn != nil {
		return cs.Conn.Close()
	}
	return nil
}

// publishMetrics invokes the PublishMetricsRequest on the clientserver object.
func (cs *clientServer) publishMetrics() {
	if cs.publishTicker == nil {
		log.Debug("publish ticker uninitialized")
		return
	}
	for range cs.publishTicker.C {
		metadata, taskMetrics, err := cs.statsEngine.GetInstanceMetrics()
		if err != nil {
			log.Warn("Error getting instance metrics", "err", err)
			return
		}

		if *metadata.Idle {
			// Idle instance, send message and return.
			cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, taskMetrics))
			return
		}

		var messageTaskMetrics []*ecstcs.TaskMetric
		for i := range taskMetrics {
			messageTaskMetrics = append(messageTaskMetrics, taskMetrics[i])
			if (i+1)%tasksInMessage == 0 {
				// Construct payload with tasksInMessage number of task metrics and send to backend.
				cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, messageTaskMetrics))
				messageTaskMetrics = messageTaskMetrics[:0]
			}
		}

		if len(messageTaskMetrics) > 0 {
			// Send remaining task metrics to backend.
			cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, messageTaskMetrics))
		}
	}
}
<commit_msg>Don't consume messages in tcs if statsengine is empty<commit_after>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 tcsclient

import (
	"bytes"
	"fmt"
	"net/http"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/ecs_client/authv4"
	"github.com/aws/amazon-ecs-agent/agent/ecs_client/authv4/credentials"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/aws/amazon-ecs-agent/agent/stats"
	"github.com/aws/amazon-ecs-agent/agent/tcs/model/ecstcs"
	"github.com/aws/amazon-ecs-agent/agent/wsclient"
	"github.com/gorilla/websocket"
)

// tasksInMessage is the maximum number of tasks that can be sent in a message to the backend
// This is a very conservative estimate assuming max allowed string lengths for all fields.
const tasksInMessage = 10

var log = logger.ForModule("tcs client")

// clientServer implements wsclient.ClientServer interface for metrics backend.
type clientServer struct {
	statsEngine            stats.Engine
	publishTicker          *time.Ticker
	publishMetricsInterval time.Duration
	wsclient.ClientServerImpl
	signer authv4.HttpSigner
}

// New returns a client/server to bidirectionally communicate with the backend.
// The returned struct should have both 'Connect' and 'Serve' called upon it
// before being used.
func New(url string, region string, credentialProvider credentials.AWSCredentialProvider, acceptInvalidCert bool, statsEngine stats.Engine, publishMetricsInterval time.Duration) wsclient.ClientServer {
	cs := &clientServer{
		statsEngine:            statsEngine,
		publishTicker:          nil,
		publishMetricsInterval: publishMetricsInterval,
		signer:                 authv4.NewHttpSigner(region, wsclient.ServiceName, credentialProvider, nil),
	}
	cs.URL = url
	cs.Region = region
	cs.CredentialProvider = credentialProvider
	cs.AcceptInvalidCert = acceptInvalidCert
	cs.ServiceError = &tcsError{}
	cs.RequestHandlers = make(map[string]wsclient.RequestHandler)
	cs.TypeDecoder = &TcsDecoder{}
	return cs
}

// Serve begins serving requests using previously registered handlers (see
// AddRequestHandler). All request handlers should be added prior to making this
// call as unhandled requests will be discarded.
func (cs *clientServer) Serve() error {
	log.Debug("Starting websocket poll loop")
	if cs.Conn == nil {
		return fmt.Errorf("nil connection")
	}

	if cs.statsEngine == nil {
		return fmt.Errorf("uninitialized stats engine")
	}

	// Start the timer function to publish metrics to the backend.
	cs.publishTicker = time.NewTicker(cs.publishMetricsInterval)
	go cs.publishMetrics()

	return cs.ConsumeMessages()
}

// MakeRequest makes a request using the given input. Note, the input *MUST* be
// a pointer to a valid backend type that this client recognises
func (cs *clientServer) MakeRequest(input interface{}) error {
	payload, err := cs.CreateRequestMessage(input)
	if err != nil {
		return err
	}

	log.Debug("sending payload", "payload", string(payload))
	data := cs.signRequest(payload)

	// Over the wire we send something like
	// {"type":"AckRequest","message":{"messageId":"xyz"}}
	return cs.Conn.WriteMessage(websocket.TextMessage, data)
}

func (cs *clientServer) signRequest(payload []byte) []byte {
	signer := authv4.NewHttpSigner(cs.Region, "ecs", cs.CredentialProvider, nil)
	reqBody := bytes.NewBuffer(payload)
	// NewRequest never returns an error if the url parses and we just verified
	// it did above
	request, _ := http.NewRequest("GET", cs.URL, reqBody)
	signer.SignHttpRequest(request)

	var data []byte
	for k, vs := range request.Header {
		for _, v := range vs {
			data = append(data, k...)
			data = append(data, ": "...)
			data = append(data, v...)
			data = append(data, "\r\n"...)
		}
	}
	data = append(data, "\r\n"...)
	data = append(data, payload...)

	return data
}

// Close closes the underlying connection.
func (cs *clientServer) Close() error {
	if cs.publishTicker != nil {
		cs.publishTicker.Stop()
	}
	if cs.Conn != nil {
		return cs.Conn.Close()
	}
	return nil
}

// publishMetrics invokes the PublishMetricsRequest on the clientserver object.
func (cs *clientServer) publishMetrics() {
	if cs.publishTicker == nil {
		log.Debug("publish ticker uninitialized")
		return
	}
	for range cs.publishTicker.C {
		metadata, taskMetrics, err := cs.statsEngine.GetInstanceMetrics()
		if err != nil {
			log.Warn("Error getting instance metrics", "err", err)
			return
		}

		if *metadata.Idle {
			// Idle instance, send message and return.
			cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, taskMetrics))
			return
		}

		var messageTaskMetrics []*ecstcs.TaskMetric
		for i := range taskMetrics {
			messageTaskMetrics = append(messageTaskMetrics, taskMetrics[i])
			if (i+1)%tasksInMessage == 0 {
				// Construct payload with tasksInMessage number of task metrics and send to backend.
				cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, messageTaskMetrics))
				messageTaskMetrics = messageTaskMetrics[:0]
			}
		}

		if len(messageTaskMetrics) > 0 {
			// Send remaining task metrics to backend.
			cs.MakeRequest(ecstcs.NewPublishMetricsRequest(metadata, messageTaskMetrics))
		}
	}
}
<|endoftext|>