~juju-qa/juju-core/1.16-packaging

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/environs/localstorage/storage.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-10-10 18:07:45 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20131010180745-wuo0vv7hq7faavdk
Tags: 1.16.0-0ubuntu1
New upstream stable release (LP: #1219879).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2013 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
 
package localstorage
5
 
 
6
 
import (
7
 
        "fmt"
8
 
        "io"
9
 
        "io/ioutil"
10
 
        "net/http"
11
 
        "sort"
12
 
        "strings"
13
 
 
14
 
        "launchpad.net/juju-core/environs"
15
 
        "launchpad.net/juju-core/errors"
16
 
        "launchpad.net/juju-core/utils"
17
 
)
18
 
 
19
 
// storage implements the environs.Storage interface.
20
 
type storage struct {
21
 
        baseURL string
22
 
}
23
 
 
24
 
// Client returns a storage object that will talk to the storage server
25
 
// at the given network address (see Serve)
26
 
func Client(addr string) environs.Storage {
27
 
        return &storage{
28
 
                baseURL: fmt.Sprintf("http://%s/", addr),
29
 
        }
30
 
}
31
 
 
32
 
// Get opens the given storage file and returns a ReadCloser
33
 
// that can be used to read its contents. It is the caller's
34
 
// responsibility to close it after use. If the name does not
35
 
// exist, it should return a *NotFoundError.
36
 
func (s *storage) Get(name string) (io.ReadCloser, error) {
37
 
        url, err := s.URL(name)
38
 
        if err != nil {
39
 
                return nil, err
40
 
        }
41
 
        resp, err := http.Get(url)
42
 
        if err != nil {
43
 
                return nil, err
44
 
        }
45
 
        if resp.StatusCode != http.StatusOK {
46
 
                return nil, errors.NotFoundf("file %q", name)
47
 
        }
48
 
        return resp.Body, nil
49
 
}
50
 
 
51
 
// List lists all names in the storage with the given prefix, in
52
 
// alphabetical order. The names in the storage are considered
53
 
// to be in a flat namespace, so the prefix may include slashes
54
 
// and the names returned are the full names for the matching
55
 
// entries.
56
 
func (s *storage) List(prefix string) ([]string, error) {
57
 
        url, err := s.URL(prefix)
58
 
        if err != nil {
59
 
                return nil, err
60
 
        }
61
 
        resp, err := http.Get(url + "*")
62
 
        if err != nil {
63
 
                return nil, err
64
 
        }
65
 
        if resp.StatusCode != http.StatusOK {
66
 
                return nil, fmt.Errorf("%s", resp.Status)
67
 
        }
68
 
        defer resp.Body.Close()
69
 
        body, err := ioutil.ReadAll(resp.Body)
70
 
        if err != nil {
71
 
                return nil, err
72
 
        }
73
 
        if len(body) == 0 {
74
 
                return nil, nil
75
 
        }
76
 
        names := strings.Split(string(body), "\n")
77
 
        sort.Strings(names)
78
 
        return names, nil
79
 
}
80
 
 
81
 
// URL returns an URL that can be used to access the given storage file.
82
 
func (s *storage) URL(name string) (string, error) {
83
 
        return s.baseURL + name, nil
84
 
}
85
 
 
86
 
// ConsistencyStrategy is specified in the StorageReader interface.
87
 
func (s *storage) ConsistencyStrategy() utils.AttemptStrategy {
88
 
        return utils.AttemptStrategy{}
89
 
}
90
 
 
91
 
// Put reads from r and writes to the given storage file.
92
 
// The length must be set to the total length of the file.
93
 
func (s *storage) Put(name string, r io.Reader, length int64) error {
94
 
        url, err := s.URL(name)
95
 
        if err != nil {
96
 
                return err
97
 
        }
98
 
        // Here we wrap up the reader.  For some freaky unexplainable reason, the
99
 
        // http library will call Close on the reader if it has a Close method
100
 
        // available.  Since we sometimes reuse the reader, especially when
101
 
        // putting tools, we don't want Close called.  So we wrap the reader in a
102
 
        // struct so the Close method is not exposed.
103
 
        justReader := struct{ io.Reader }{r}
104
 
        req, err := http.NewRequest("PUT", url, justReader)
105
 
        if err != nil {
106
 
                return err
107
 
        }
108
 
        req.Header.Set("Content-Type", "application/octet-stream")
109
 
        resp, err := http.DefaultClient.Do(req)
110
 
        if err != nil {
111
 
                return err
112
 
        }
113
 
        if resp.StatusCode != 201 {
114
 
                return fmt.Errorf("%d %s", resp.StatusCode, resp.Status)
115
 
        }
116
 
        return nil
117
 
}
118
 
 
119
 
// Remove removes the given file from the environment's
120
 
// storage. It should not return an error if the file does
121
 
// not exist.
122
 
func (s *storage) Remove(name string) error {
123
 
        url, err := s.URL(name)
124
 
        if err != nil {
125
 
                return err
126
 
        }
127
 
        req, err := http.NewRequest("DELETE", url, nil)
128
 
        if err != nil {
129
 
                return err
130
 
        }
131
 
        resp, err := http.DefaultClient.Do(req)
132
 
        if err != nil {
133
 
                return err
134
 
        }
135
 
        if resp.StatusCode != http.StatusOK {
136
 
                return fmt.Errorf("%d %s", resp.StatusCode, resp.Status)
137
 
        }
138
 
        return nil
139
 
}
140
 
 
141
 
func (s *storage) RemoveAll() error {
142
 
        return environs.RemoveAll(s)
143
 
}