~ubuntu-branches/ubuntu/saucy/juju-core/saucy-proposed

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-07-11 17:18:27 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130711171827-vjqkg40r0dlf7ys2
Tags: 1.11.2-0ubuntu1
* New upstream release.
* Make juju-core the default juju (LP: #1190634):
  - d/control: Add virtual package juju -> juju-core.
  - d/juju-core.postinst.in: Bump priority of alternatives over that of
    python juju packages.
* Enable for all architectures (LP: #1172505):
  - d/control: Version BD on golang-go to >= 2:1.1.1 to ensure CGO
    support for non-x86 archs, make juju-core Arch: any.
  - d/README.source: Dropped - no longer required.
* d/watch: Updated for new upstream tarball naming.

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"
 
11
        "net/http"
 
12
        "os"
 
13
        "path/filepath"
 
14
        "sort"
 
15
        "strings"
 
16
)
 
17
 
 
18
// storageBackend provides HTTP access to a defined path. The local
 
19
// provider otimally would use a much simpler Storage, but this
 
20
// code may be useful in storage-free environs. Here it requires
 
21
// additional authentication work before it's viable.
 
22
type storageBackend struct {
 
23
        dir string
 
24
}
 
25
 
 
26
// ServeHTTP handles the HTTP requests to the container.
 
27
func (s *storageBackend) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 
28
        switch req.Method {
 
29
        case "GET":
 
30
                if strings.HasSuffix(req.URL.Path, "*") {
 
31
                        s.handleList(w, req)
 
32
                } else {
 
33
                        s.handleGet(w, req)
 
34
                }
 
35
        case "PUT":
 
36
                s.handlePut(w, req)
 
37
        case "DELETE":
 
38
                s.handleDelete(w, req)
 
39
        default:
 
40
                http.Error(w, "method "+req.Method+" is not supported", http.StatusMethodNotAllowed)
 
41
        }
 
42
}
 
43
 
 
44
// handleGet returns a storage file to the client.
 
45
func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) {
 
46
        data, err := ioutil.ReadFile(filepath.Join(s.dir, req.URL.Path))
 
47
        if err != nil {
 
48
                http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)
 
49
                return
 
50
        }
 
51
        w.Header().Set("Content-Type", "application/octet-stream")
 
52
        w.Write(data)
 
53
}
 
54
 
 
55
// handleList returns the file names in the storage to the client.
 
56
func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) {
 
57
        fp := filepath.Join(s.dir, req.URL.Path)
 
58
        dir, prefix := filepath.Split(fp)
 
59
        names, err := readDirs(dir, prefix[:len(prefix)-1], len(s.dir)+1)
 
60
        if err != nil {
 
61
                http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)
 
62
                return
 
63
        }
 
64
        sort.Strings(names)
 
65
        data := []byte(strings.Join(names, "\n"))
 
66
        w.Header().Set("Content-Type", "application/octet-stream")
 
67
        w.Write(data)
 
68
}
 
69
 
 
70
// readDirs reads the directory hierarchy and compares the found
 
71
// names with the given prefix.
 
72
func readDirs(dir, prefix string, start int) ([]string, error) {
 
73
        names := []string{}
 
74
        fis, err := ioutil.ReadDir(dir)
 
75
        if err != nil {
 
76
                return nil, err
 
77
        }
 
78
        for _, fi := range fis {
 
79
                name := fi.Name()
 
80
                if strings.HasPrefix(name, prefix) {
 
81
                        if fi.IsDir() {
 
82
                                dnames, err := readDirs(filepath.Join(dir, name), prefix, start)
 
83
                                if err != nil {
 
84
                                        return nil, err
 
85
                                }
 
86
                                names = append(names, dnames...)
 
87
                                continue
 
88
                        }
 
89
                        fullname := filepath.Join(dir, name)[start:]
 
90
                        names = append(names, fullname)
 
91
                }
 
92
        }
 
93
        return names, nil
 
94
}
 
95
 
 
96
// handlePut stores data from the client in the storage.
 
97
func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) {
 
98
        fp := filepath.Join(s.dir, req.URL.Path)
 
99
        dir, _ := filepath.Split(fp)
 
100
        err := os.MkdirAll(dir, 0777)
 
101
        if err != nil {
 
102
                http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
 
103
                return
 
104
        }
 
105
        out, err := os.Create(fp)
 
106
        if err != nil {
 
107
                http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
 
108
                return
 
109
        }
 
110
        defer out.Close()
 
111
        if _, err := io.Copy(out, req.Body); err != nil {
 
112
                http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
 
113
                return
 
114
        }
 
115
        w.WriteHeader(http.StatusCreated)
 
116
}
 
117
 
 
118
// handleDelete removes a file from the storage.
 
119
func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) {
 
120
        fp := filepath.Join(s.dir, req.URL.Path)
 
121
        if err := os.Remove(fp); err != nil && !os.IsNotExist(err) {
 
122
                http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
 
123
                return
 
124
        }
 
125
        w.WriteHeader(http.StatusOK)
 
126
}
 
127
 
 
128
// Serve runs a storage server on the given network address, storing
 
129
// data under the given directory.  It returns the network listener.
 
130
// This can then be attached to with Client.
 
131
func Serve(addr, dir string) (net.Listener, error) {
 
132
        backend := &storageBackend{
 
133
                dir: dir,
 
134
        }
 
135
        info, err := os.Stat(dir)
 
136
        if err != nil {
 
137
                return nil, fmt.Errorf("cannot stat directory: %v", err)
 
138
        }
 
139
        if !info.IsDir() {
 
140
                return nil, fmt.Errorf("%q is not a directory", dir)
 
141
        }
 
142
        listener, err := net.Listen("tcp", addr)
 
143
        if err != nil {
 
144
                return nil, fmt.Errorf("cannot start listener: %v", err)
 
145
        }
 
146
        mux := http.NewServeMux()
 
147
        mux.Handle("/", backend)
 
148
        go http.Serve(listener, mux)
 
149
        return listener, nil
 
150
}