diff --git a/pkg/blobserver/s3/s3.go b/pkg/blobserver/s3/s3.go index c47cac1a9..9767dc5ee 100644 --- a/pkg/blobserver/s3/s3.go +++ b/pkg/blobserver/s3/s3.go @@ -52,9 +52,17 @@ func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (storage blobserv // TODO: skip this check if a file // ~/.camli/.configcheck/sha1-("IS GOOD: s3: sha1(access key + // secret key)") exists and has recent time? - if _, err := client.Buckets(); err != nil { + buckets, err := client.Buckets() + if err != nil { return nil, fmt.Errorf("Failed to get bucket list from S3: %v", err) } + haveBucket := make(map[string]bool) + for _, b := range buckets { + haveBucket[b.Name] = true + } + if !haveBucket[sto.bucket] { + return nil, fmt.Errorf("S3 bucket %q doesn't exist. Create it first at https://console.aws.amazon.com/s3/home") + } } return sto, nil } diff --git a/pkg/misc/amazon/s3/client.go b/pkg/misc/amazon/s3/client.go index 8332f11d5..44e0bb199 100644 --- a/pkg/misc/amazon/s3/client.go +++ b/pkg/misc/amazon/s3/client.go @@ -71,13 +71,20 @@ func (c *Client) Buckets() ([]*Bucket, error) { if res.StatusCode != 200 { return nil, fmt.Errorf("s3: Unexpected status code %d fetching bucket list", res.StatusCode) } - slurp, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, fmt.Errorf("s3: Error reading Buckets response: %v", err) + return parseListAllMyBuckets(res.Body) +} + +func parseListAllMyBuckets(r io.Reader) ([]*Bucket, error) { + type allMyBuckets struct { + Buckets struct { + Bucket []*Bucket + } } - // TODO: parse this XML - log.Printf("s3: TODO: parse bucket list: %q", slurp) - return nil, nil + var res allMyBuckets + if err := xml.NewDecoder(r).Decode(&res); err != nil { + return nil, err + } + return res.Buckets.Bucket, nil } // Returns 0, os.ErrNotExist if not on S3, otherwise reterr is real. diff --git a/pkg/misc/amazon/s3/client_test.go b/pkg/misc/amazon/s3/client_test.go index d4da597c0..be3f6be5e 100644 --- a/pkg/misc/amazon/s3/client_test.go +++ b/pkg/misc/amazon/s3/client_test.go @@ -19,6 +19,8 @@ package s3 import ( "net/http" "os" + "reflect" + "strings" "testing" ) @@ -56,4 +58,30 @@ func TestMarker(t *testing.T) { t.Errorf("marker(%q) = %q; want %q", tt.s, got, tt.want) } } -} \ No newline at end of file +} + +func TestParseBuckets(t *testing.T) { + res := "\nownerIDFieldbobDisplayNamebucketOne2006-06-21T07:04:31.000ZbucketTwo2006-06-21T07:04:32.000Z" + buckets, err := parseListAllMyBuckets(strings.NewReader(res)) + if err != nil { + t.Fatal(err) + } + if g, w := len(buckets), 2; g != w { + t.Errorf("num parsed buckets = %d; want %d", g, w) + } + want := []*Bucket{ + {Name: "bucketOne", CreationDate: "2006-06-21T07:04:31.000Z"}, + {Name: "bucketTwo", CreationDate: "2006-06-21T07:04:32.000Z"}, + } + dump := func(v []*Bucket) { + for i, b := range v { + t.Logf("Bucket #%d: %#v", i, b) + } + } + if !reflect.DeepEqual(buckets, want) { + t.Error("mismatch; GOT:") + dump(buckets) + t.Error("WANT:") + dump(want) + } +}