kill the queue

This commit is contained in:
rubyist 2014-09-23 10:49:33 -04:00
parent 069e96f179
commit 0e217dffbf
14 changed files with 6 additions and 1128 deletions

@ -1 +0,0 @@
language: go

@ -1,56 +0,0 @@
# Simple UUIDs
[![Build Status][1]][2]
[1]: https://secure.travis-ci.org/streadway/simpleuuid.png
[2]: http://www.travis-ci.org/streadway/simpleuuid
This implements a variant of Format 1 from [RFC 4122][rfc4122] that is intended
to be roughly sortable and play nicely as Cassandra TimeUUID keys. Much of the
inspiration comes form having used [ryanking's simple\_uuid
gem](https://github.com/ryanking/simple_uuid).
As the package implies, this is a simple UUID generator. It doesn't offer
total sorting, monotonic increments or prevent timing collisions.
This package does offer a simple combination of random and time based
uniqueness that will play nicely if you want a unique key from a Time object.
If your time objects sort with a granularity of 100 nanoseconds then the UUIDs
generated will have the same order. UUIDs with the same time have undefined
order.
# Other UUIDs
The other formats described in [RFC 4122][rfc4122] should be parsable either in
text or byte form, though will not be sortable or likely have a meaningful time
componenet.
# Contributions
Send a pull request with a tested `go fmt` change in a branch other than
`master`. Do try to organize your commits to be atomic to the changes
introduced.
# License
Copyright (C) 2012 by Sean Treadway ([streadway](http://github.com/streadway))
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[rfc4122]: http://www.ietf.org/rfc/rfc4122.txt

@ -1,263 +0,0 @@
/*
Copyright (C) 2012 by Sean Treadway ([streadway](http://github.com/streadway))
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
Implements a variant of Format 1 from RFC 4122 that is intended for stable
sorting and play nicely as Cassandra TimeUUID keys.
The other formats described in RFC 4122 should be parsable either in text or
byte form, though will not be sortable or likely have a meaningful time
componenet.
*/
package simpleuuid
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"io"
"strings"
"time"
)
const (
gregorianEpoch = 0x01B21DD213814000
size = 16
variant8 = 8 // sec. 4.1.1
version1 = 1 // sec. 4.1.3
)
var (
errLength = errors.New("mismatched UUID length")
)
/*
Byte encoded sequence in the following form:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_low |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_mid | time_hi_and_version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|clk_seq_hi_res | clk_seq_low | node (0-1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| node (2-5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type UUID []byte
type uuidTime int64
// Makes a copy of the UUID. Assumes the provided UUID is valid
func Copy(uuid UUID) UUID {
dup, _ := NewBytes(uuid)
return dup
}
// Allocates a new UUID from the given time, up to 8 bytes clock sequence and
// node data supplied by the caller. The high 4 bits of the first byte will be
// masked with the UUID variant.
//
// Byte slices shorter than 8 will be right aligned to the clock, and node
// fields. For example, if you provide 4 byte slice of 0x0a0b0c0d", the last 8
// bytes of the new UUID will be 0x08000000a0b0c0d.
func NewTimeBytes(t time.Time, bytes []byte) (UUID, error) {
if len(bytes) > size {
return nil, errLength
}
me := make([]byte, size)
ts := fromUnixNano(t.UTC().UnixNano())
// time masked with version
binary.BigEndian.PutUint32(me[0:4], uint32(ts&0xffffffff))
binary.BigEndian.PutUint16(me[4:6], uint16((ts>>32)&0xffff))
binary.BigEndian.PutUint16(me[6:8], uint16((ts>>48)&0x0fff)|version1<<12)
// right aligned remaining 8 bytes masked with variant
copy(me[8+8-len(bytes):size], bytes[:len(bytes)])
me[8] = me[8]&0x0f | variant8<<4
return UUID(me), nil
}
// Allocate a UUID from a 16 byte sequence. This can take any version,
// although versions other than 1 will not have a meaningful time component.
func NewBytes(bytes []byte) (UUID, error) {
if len(bytes) != size {
return nil, errLength
}
// Copy out this slice so not to hold a reference to the container
b := make([]byte, size)
copy(b, bytes[0:size])
return UUID(b), nil
}
// Allocate a new UUID from a time, encoding the timestamp from the UTC
// timezone and using a random value for the clock and node.
func NewTime(t time.Time) (UUID, error) {
rnd := make([]byte, 8)
n, err := io.ReadFull(rand.Reader, rnd)
if n != len(rnd) {
return nil, errLength
}
if err != nil {
return nil, err
}
return NewTimeBytes(t, rnd)
}
// Parse and allocate from a string encoded UUID like:
// "6ba7b811-9dad-11d1-80b4-00c04fd430c8". Does not validate the time, node
// or clock are reasonable values, though it is intended to round trip from a
// string to a string for all versions of UUIDs.
func NewString(s string) (UUID, error) {
normalized := strings.Replace(s, "-", "", -1)
if hex.DecodedLen(len(normalized)) != size {
return nil, errLength
}
bytes, err := hex.DecodeString(normalized)
if err != nil {
return nil, err
}
return UUID(bytes), nil
}
// The time section of the UUID in the UTC timezone
func (me UUID) Time() time.Time {
nsec := me.Nanoseconds()
return time.Unix(nsec/1e9, nsec%1e9).UTC()
}
// Returns the time_low, time_mid and time_hi sections of the UUID in 100
// nanosecond resolution since the unix Epoch. Negative values indicate
// time prior to the gregorian epoch (00:00:00.00, 15 October 1582).
func (me UUID) Nanoseconds() int64 {
time_low := uuidTime(binary.BigEndian.Uint32(me[0:4]))
time_mid := uuidTime(binary.BigEndian.Uint16(me[4:6]))
time_hi := uuidTime((binary.BigEndian.Uint16(me[6:8]) & 0x0fff))
return toUnixNano((time_low) + (time_mid << 32) + (time_hi << 48))
}
/*
The 4 bit version of the underlying UUID.
Version Description
1 The time-based version specified in RFC4122.
2 DCE Security version, with embedded POSIX UIDs.
3 The name-based version specified in RFC4122
that uses MD5 hashing.
4 The randomly or pseudo- randomly generated version
specified in RFC4122.
5 The name-based version specified in RFC4122
that uses SHA-1 hashing.
*/
func (me UUID) Version() int8 {
return int8((binary.BigEndian.Uint16(me[6:8]) & 0xf000) >> 12)
}
/*
The 3 bit variant of the underlying UUID.
Msb0 Msb1 Msb2 Description
0 x x Reserved, NCS backward compatibility.
1 0 x The variant specified in RFC4122.
1 1 0 Reserved, Microsoft Corporation backward compatibility
1 1 1 Reserved for future definition.
*/
func (me UUID) Variant() int8 {
return int8((binary.BigEndian.Uint16(me[8:10]) & 0xe000) >> 13)
}
// The timestamp in hex encoded form.
func (me UUID) String() string {
return hex.EncodeToString(me[0:4]) + "-" +
hex.EncodeToString(me[4:6]) + "-" +
hex.EncodeToString(me[6:8]) + "-" +
hex.EncodeToString(me[8:10]) + "-" +
hex.EncodeToString(me[10:16])
}
// Stable comparison, first of the times then of the node values.
func (me UUID) Compare(other UUID) int {
nsMe := me.Nanoseconds()
nsOther := other.Nanoseconds()
if nsMe > nsOther {
return 1
} else if nsMe < nsOther {
return -1
}
return bytes.Compare(me[8:], other[8:])
}
// The underlying byte slice. Treat the slice returned as immutable.
func (me UUID) Bytes() []byte {
return me
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (me *UUID) UnmarshalJSON(b []byte) error {
var field string
if err := json.Unmarshal(b, &field); err != nil {
return err
}
uuid, err := NewString(field)
if err != nil {
return err
}
*me = uuid
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (me UUID) MarshalJSON() ([]byte, error) {
return []byte(`"` + me.String() + `"`), nil
}
// Utility functions
func fromUnixNano(ns int64) uuidTime {
return uuidTime((ns / 100) + gregorianEpoch)
}
func toUnixNano(t uuidTime) int64 {
return int64((t - gregorianEpoch) * 100)
}

@ -1,398 +0,0 @@
/*
Copyright (C) 2012 by Sean Treadway ([streadway](http://github.com/streadway))
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package simpleuuid
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"testing/quick"
"time"
)
var (
zero = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
url = []byte{0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
urlString = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
)
func TestNewBytes(t *testing.T) {
_, err := NewBytes(zero)
if err != nil {
t.Error("Fail", err)
}
}
func TestNewTimeRoundTrip(t *testing.T) {
now := time.Now()
uuid, err := NewTime(now)
if err != nil {
t.Error(err)
}
then := uuid.Time()
if now.UnixNano()/100 != then.UnixNano()/100 {
t.Errorf("UUID should parse and generate time based with 100ns precision. want %v, got %v", now.UTC(), then)
}
}
func TestNewString(t *testing.T) {
uuid1, err := NewString(urlString)
if err != nil {
t.Error(err)
}
if uuid1.String() != urlString {
t.Error("Strings do not match", uuid1.String(), urlString)
}
uuid2, err := NewString(strings.Replace(urlString, "-", "", -1))
if err != nil {
t.Error(err)
}
if uuid2.String() != uuid1.String() {
t.Error("Stripping dashes should not affect string parsing", uuid1, uuid2)
}
}
func TestBadNewString(t *testing.T) {
_, err := NewString("0000")
if err == nil {
t.Error("Should fail on short GUID")
}
_, err = NewString("00000000000000000000000000000000000000000")
if err == nil {
t.Error("Should fail on long GUID")
}
_, err = NewString("0000------------------------0000")
if err == nil {
t.Error("Should fail on missing digits")
}
_, err = NewString("-0--000-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0--0--")
if err != nil {
t.Error("Should ignore dashes")
}
}
func TestFormatString(t *testing.T) {
uuid, err := NewBytes(url)
if err != nil {
t.Error(err)
}
if uuid.String() != urlString {
t.Error("UUID should have correct string", uuid.String())
}
}
func TestVersion(t *testing.T) {
url, err := NewBytes(url)
if err != nil {
t.Error(err)
}
if url.Version() != 0x1 {
t.Error("Not recognized as a url version", url.Version())
}
time, err := NewTime(time.Now())
if err != nil {
t.Error(err)
}
if time.Version() != 0x1 {
t.Error("Not recognized as a time version", url.Version())
}
}
func TestVariant(t *testing.T) {
url, err := NewBytes(url)
if err != nil {
t.Error(err)
}
if url.Variant() != 0x4 {
t.Error("Variant should be 4", url.Variant())
}
time, err := NewTime(time.Now())
if err != nil {
t.Error(err)
}
if time.Variant() != 0x4 {
t.Error("Variant should be 4", url.Variant())
}
}
func TestBytes(t *testing.T) {
url1, err := NewBytes(url)
if err != nil {
t.Error(err)
}
url2, err := NewBytes(url1.Bytes())
if err != nil {
t.Error(err)
}
if url1.String() != url2.String() {
t.Error("Bytes not equal", url1, url2)
}
}
func TestUnmarshalJSON(t *testing.T) {
b := fmt.Sprintf(`{"uuid":"%s"}`, urlString)
s := new(struct{ Uuid UUID })
if err := json.Unmarshal([]byte(b), s); err != nil {
t.Error(err)
}
got := s.Uuid.String()
want := urlString
if got != want {
t.Errorf("UUID Mismatch: %s, %s", got, want)
}
}
func TestMarshalJSON(t *testing.T) {
uuid, err := NewString(urlString)
if err != nil {
t.Error(err)
}
b, err := json.Marshal(struct{ Uuid UUID }{uuid})
if err != nil {
t.Error(err)
}
got := string(b)
want := fmt.Sprintf(`{"Uuid":"%s"}`, urlString)
if got != want {
t.Errorf("Output mismatch: %s, %s", got, want)
}
}
func TestCompare(t *testing.T) {
u1, err := NewBytes(url)
if err != nil {
t.Error(err)
}
u2, err := NewBytes(url)
if err != nil {
t.Error(err)
}
u3, err := NewBytes(zero)
if err != nil {
t.Error(err)
}
if bytes.Compare(u1, u2) != 0 {
t.Error("Should be equal", u1, u2)
}
if bytes.Compare(u1, u3) <= 0 {
t.Error("Should be greater", u1, u3)
}
if bytes.Compare(u3, u1) >= 0 {
t.Error("Should be less", u1, u3)
}
}
// Conditions
func TestUnixTimeAt100NanoResolution(t *testing.T) {
f := func(sec, nsec uint32) bool {
now := time.Unix(int64(sec), int64(nsec))
u1, _ := NewTime(now)
return u1.Time().UnixNano()/100 == now.UnixNano()/100
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestInequalityForTimeWithRandom(t *testing.T) {
f := func(sec, nsec uint32) bool {
time := time.Unix(int64(sec), int64(nsec))
u1, _ := NewTime(time)
u2, _ := NewTime(time)
return u1.Compare(u2) != 0
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestEqualityForTimeWithBytes(t *testing.T) {
f := func(sec, nsec uint32, b byte) bool {
time := time.Unix(int64(sec), int64(nsec))
u1, _ := NewTimeBytes(time, []byte{b, b, b, b, b, b, b, b})
u2, _ := NewTimeBytes(time, []byte{b, b, b, b, b, b, b, b})
return u1.Compare(u2) == 0
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestRoundTripOfTimeBytes(t *testing.T) {
f := func(sec, nsec uint32, b byte) bool {
time := time.Unix(int64(sec), int64(nsec))
bs := []byte{b, b, b, b, b, b, b, b}
ut, _ := NewTimeBytes(time, bs)
ub, _ := NewBytes([]byte(ut))
return bytes.Compare(ut[8:], ub[8:]) == 0
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestZeroPaddedRightAlignmentOfTimeBytes(t *testing.T) {
f := func(b byte, l uint) bool {
bs := make([]byte, (l%8)+1)
for i, _ := range bs {
bs[i] = b
}
u, err := NewTimeBytes(time.Now(), bs)
if err != nil {
return false
}
// check masked bytes right to left
for i, j := 15, len(bs)-1; i >= 8; i, j = i-1, j-1 {
if j >= 0 {
if bs[j]&0x0f != u[i]&0x0f {
t.Log("expected right aligned %d to equal %d", j, i)
return false
}
} else {
if u[i]&0x0f != 0 {
t.Log("expected right %d be zero", i)
return false
}
}
}
return true
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestPositiveTime(t *testing.T) {
f := func(sec, nsec uint32) bool {
time := time.Unix(int64(sec), int64(nsec))
u1, _ := NewTime(time)
return u1.Nanoseconds() > 0
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestOrdering(t *testing.T) {
f := func(sec1, nsec1, sec2, nsec2 uint32) bool {
time1 := time.Unix(int64(sec1), int64(nsec1))
time2 := time.Unix(int64(sec2), int64(nsec2))
u1, _ := NewTime(time1)
u2, _ := NewTime(time2)
if time1.UnixNano() > time2.UnixNano() {
return u1.Compare(u2) > 0
}
return u1.Compare(u2) < 0
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
// Version 1 + Variant 8
// xxxxxxxx-xxxx-1xxx-yxxx-xxxxxxxxxxxx y::{8 9 a b}
// 012345678901234567890123456789012345
func hasVersionAndVariantDigitsInString(u UUID) bool {
s := u.String()
return s[14] == '1' &&
(s[19] == '8' ||
s[19] == '9' ||
s[19] == 'a' ||
s[19] == 'b')
}
func TestStringVersionAndVariantForNewTime(t *testing.T) {
f := func(sec, nsec uint32) bool {
u, _ := NewTime(time.Unix(int64(sec), int64(nsec)))
return hasVersionAndVariantDigitsInString(u)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestStringVersionAndVariantForNewTimeBytes(t *testing.T) {
f := func(sec, nsec uint32, b byte) bool {
u, _ := NewTimeBytes(time.Unix(int64(sec), int64(nsec)), []byte{b, b, b, b, b, b, b, b})
return hasVersionAndVariantDigitsInString(u)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}

1
Godeps

@ -5,5 +5,4 @@ github.com/kr/text 6807e777504f54ad073ecef66747de1582
github.com/olekukonko/ts ecf753e7c962639ab5a1fb46f7da627d4c0a04b8
github.com/spf13/cobra 864687ae689edc28688c67edef47e3d2ad651a1b
github.com/spf13/pflag 463bdc838f2b35e9307e91d480878bda5fff7232
github.com/streadway/simpleuuid 6617b501e485b77e61b98cd533aefff9e258b5a7
github.com/technoweenie/go-contentaddressable 38171def3cd15e3b76eb156219b3d48704643899

@ -1,44 +0,0 @@
package commands
import (
"github.com/github/git-media/gitmedia"
"github.com/github/git-media/queuedir"
"github.com/spf13/cobra"
"os"
"path/filepath"
"strings"
)
var (
queuesCmd = &cobra.Command{
Use: "queues",
Short: "Show the status of the internal Git Media queues",
Run: queuesCommand,
}
)
func queuesCommand(cmd *cobra.Command, args []string) {
err := gitmedia.WalkQueues(func(name string, queue *queuedir.Queue) error {
wd, _ := os.Getwd()
Print(name)
return queue.Walk(func(id string, body []byte) error {
parts := strings.Split(string(body), ":")
if len(parts) == 2 {
absPath := filepath.Join(gitmedia.LocalWorkingDir, parts[1])
relPath, _ := filepath.Rel(wd, absPath)
Print(" " + relPath)
} else {
Print(" " + parts[0])
}
return nil
})
})
if err != nil {
Panic(err, "Error walking queues")
}
}
func init() {
RootCmd.AddCommand(queuesCmd)
}

@ -91,13 +91,12 @@ Now, add a file:
```
$ git add my.zip
# confirm the zip was added to the "upload" queue
$ git media queues
upload
my.zip
# confirm the zip was added to git media
$ git media ls-files
my.zip
```
When you can see files being added to the upload queue, you can commit like
When you can see files being added to git media, you can commit like
normal. After committing, `git show` will show the file's meta data:
$ git show
@ -117,7 +116,7 @@ normal. After committing, `git show` will show the file's meta data:
+84ff327f80500d3266bd830891ede1e4fd18b9169936a066573f9b230597a696
\ No newline at end of file
Now, when you run `git push`, all queued files to will be synced to the
Now, when you run `git push`, added media files will be synced to the
Git Media endpoint.
$ git push origin master

@ -16,6 +16,6 @@ Git attributes.
## SEE ALSO
git-media-init(1), git-media-push(1), git-media-queues(1), gitattributes(5).
git-media-init(1), git-media-push(1), gitattributes(5).
Part of the git-media(1) suite.

@ -1,18 +0,0 @@
git-media-queues(1) - View queued content to be pushed.
=======================================================
## SYNOPSIS
`git media queues`
## DESCRIPTION
The "queues" command displays this list of objects waiting to be pushed to the
remote Git Media endpoint. These objects are usually added by
git-media-clean(1).
## SEE ALSO
git-media-clean(1).
Part of the git-media(1) suite.

@ -30,8 +30,6 @@ commands and low level ("plumbing") commands.
View and modify Git Media paths in Git attributes.
* git-media-push(1):
Push queued large files to the Git Media endpoint.
* git-media-queues(1):
View queued content to be pushed.
### Low level commands (plumbing)

@ -91,7 +91,6 @@ func init() {
LocalLogDir = filepath.Join(LocalMediaDir, "logs")
LocalLinkDir = filepath.Join(LocalMediaDir, "objects")
TempDir = filepath.Join(LocalMediaDir, "tmp")
queueDir = setupQueueDir()
if err := os.MkdirAll(TempDir, 0744); err != nil {
panic(fmt.Errorf("Error trying to create temp directory in '%s': %s", TempDir, err))

@ -1,60 +0,0 @@
package gitmedia
import (
"github.com/github/git-media/queuedir"
"path/filepath"
)
func QueueUpload(sha, filename string) error {
fileBody := sha
if filename != "" {
fileBody += ":" + filename
}
q, err := UploadQueue()
if err != nil {
return err
}
_, err = q.AddString(fileBody)
return err
}
func WalkQueues(cb func(name string, queue *queuedir.Queue) error) error {
var err error
for name, queuefunc := range queues {
q, err := queuefunc()
if err == nil {
err = cb(name, q)
}
if err != nil {
return err
}
}
return err
}
func UploadQueue() (*queuedir.Queue, error) {
if uploadQueue == nil {
q, err := queueDir.Queue("upload")
if err != nil {
return nil, err
}
uploadQueue = q
}
return uploadQueue, nil
}
func setupQueueDir() *queuedir.QueueDir {
return queuedir.New(filepath.Join(LocalMediaDir, "queue"))
}
var (
queues = map[string]func() (*queuedir.Queue, error){
"upload": UploadQueue,
}
queueDir *queuedir.QueueDir
uploadQueue *queuedir.Queue
)

@ -1,124 +0,0 @@
// Package queue implements a simple file system queue. Jobs are stored as
// files in a directory. Loosely implements something like maildir, without
// any specific code for dealing with email.
package queuedir
import (
"bytes"
"fmt"
"github.com/streadway/simpleuuid"
"io"
"io/ioutil"
"os"
"path/filepath"
"time"
)
type QueueDir struct {
Path string
}
func New(path string) *QueueDir {
return &QueueDir{Path: path}
}
func (q *QueueDir) Queue(name string) (*Queue, error) {
qu := &Queue{name, filepath.Join(q.Path, name), q}
err := os.MkdirAll(qu.Path, 0777)
return qu, err
}
type Queue struct {
Name string
Path string
Dir *QueueDir
}
type WalkFunc func(id string, body []byte) error
func (q *Queue) Add(reader io.Reader) (string, error) {
uuid, err := simpleuuid.NewTime(time.Now())
if err != nil {
return "", err
}
id := uuid.String()
file, err := os.Create(q.FullPath(id))
if err == nil {
defer file.Close()
_, err = io.Copy(file, reader)
}
return id, err
}
func (q *Queue) Count() (int, error) {
total := 0
err := filepath.Walk(q.Path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if err != nil {
return err
}
total += 1
return nil
})
return total, err
}
func (q *Queue) Walk(cb WalkFunc) error {
return filepath.Walk(q.Path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if err != nil {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
body, err := ioutil.ReadAll(file)
if err != nil {
return err
}
return cb(filepath.Base(path), body)
})
}
func (q *Queue) AddString(body string) (string, error) {
return q.Add(bytes.NewBufferString(body))
}
func (q *Queue) AddBytes(body []byte) (string, error) {
return q.Add(bytes.NewBuffer(body))
}
func (q *Queue) Move(newqueue *Queue, id string) error {
return os.Rename(q.FullPath(id), newqueue.FullPath(id))
}
func (q *Queue) Del(id string) error {
full := q.FullPath(id)
stat, err := os.Stat(full)
if err != nil {
return err
}
if stat.IsDir() {
return fmt.Errorf("%s in %s is a directory", id, q.Path)
}
return os.Remove(full)
}
func (q *Queue) FullPath(id string) string {
return filepath.Join(q.Path, id)
}

@ -1,153 +0,0 @@
package queuedir
import (
"github.com/bmizerany/assert"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestAdd(t *testing.T) {
q := Setup(t)
defer q.Teardown()
id, err := q.Queue.AddString("BOOM")
if err != nil {
t.Fatalf("Cannot add to queue: %s", err)
}
assertExist(t, q.Queue, id)
file, err := os.Open(filepath.Join(q.Queue.Path, id))
if err != nil {
t.Fatalf("Cannot open file: %s", err)
}
by, err := ioutil.ReadAll(file)
if err != nil {
t.Fatalf("Cannot read file: %s", err)
}
assert.Equal(t, "BOOM", string(by))
}
func TestWalk(t *testing.T) {
q := Setup(t)
defer q.Teardown()
id1, err := q.Queue.AddString("BOOM0")
if err != nil {
t.Fatalf("Cannot add to queue: %s", err)
}
id2, err := q.Queue.AddString("BOOM1")
if err != nil {
t.Fatalf("Cannot add to queue: %s", err)
}
seen := make(map[string]bool)
q.Queue.Walk(func(id string, body []byte) error {
if err != nil {
t.Errorf("Error reading queue data for %s: %s", id, err)
}
seen[id] = true
if id == id1 {
assert.Equal(t, id1, id)
} else if id == id2 {
assert.Equal(t, id2, id)
} else {
t.Errorf("Weird ID: %s", id)
}
return nil
})
assert.Equal(t, 2, len(seen))
}
func TestMove(t *testing.T) {
q := Setup(t)
defer q.Teardown()
id, err := q.Queue.AddString("BOOM")
if err != nil {
t.Fatalf("Cannot add to queue: %s", err)
}
assertExist(t, q.Queue, id)
queue2, err := q.Dir.Queue("test2")
if err != nil {
t.Fatalf("Cannot create %s queue: %s", queue2.Name, err)
}
err = q.Queue.Move(queue2, id)
if err != nil {
t.Fatalf("Cannot move from queue %s to %s: %s", q.Queue.Name, queue2.Name, err)
}
assertNotExist(t, q.Queue, id)
assertExist(t, queue2, id)
}
func TestDel(t *testing.T) {
q := Setup(t)
defer q.Teardown()
id, err := q.Queue.AddString("BOOM")
if err != nil {
t.Fatalf("Cannot add to queue: %s", err)
}
assertExist(t, q.Queue, id)
err = q.Queue.Del(id)
if err != nil {
t.Fatalf("Error deleting from queue: %s", err)
}
assertNotExist(t, q.Queue, id)
}
type QueueTest struct {
Dir *QueueDir
Queue *Queue
}
func Setup(t *testing.T) *QueueTest {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("Cannot get current working dir: %s", err)
}
qdir := New(filepath.Join(wd, "test_queuedir"))
q, err := qdir.Queue("test")
if err != nil {
t.Fatalf("Cannot create test queue: %s", err)
}
return &QueueTest{qdir, q}
}
func (t *QueueTest) Teardown() {
os.RemoveAll(t.Dir.Path)
}
func assertExist(t *testing.T, q *Queue, id string) {
if !fileExists(q, id) {
t.Fatalf("%s does not exist in queue %s", id, q.Name)
}
}
func assertNotExist(t *testing.T, q *Queue, id string) {
if fileExists(q, id) {
t.Fatalf("%s exists in queue %s", id, q.Name)
}
}
func fileExists(q *Queue, id string) bool {
_, err := os.Stat(q.FullPath(id))
return err == nil
}