~natefinch/juju-core/jameinel-1.18-panic-parsing-jenv-1312136

« back to all changes in this revision

Viewing changes to testing/filetesting/filetesting.go

[r=wallyworld],[bug=1303697] Backport trunk r2606
lp:~fwereade/juju-core/uniter-relation-states

worker/uniter: startup with correct relation state

The uniter's runListener is not started until after relation state is set
up; and relation state setup is fixed to use true relation state from the
API server rather than the local best guess, which is inaccurate when any
joined relation has never seen a remote unit.

This involves a new API, which the unit agent will wait for, in case it
connects to an API server which hasn't yet been upgraded.

https://codereview.appspot.com/85670046/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013, 2014 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENCE file for details.
 
3
 
 
4
package filetesting
 
5
 
 
6
import (
 
7
        "io/ioutil"
 
8
        "os"
 
9
        "path/filepath"
 
10
 
 
11
        jc "github.com/juju/testing/checkers"
 
12
        gc "launchpad.net/gocheck"
 
13
 
 
14
        "launchpad.net/juju-core/utils"
 
15
)
 
16
 
 
17
// Entry represents a filesystem entity that can be created; and whose
 
18
// correctness can be verified.
 
19
type Entry interface {
 
20
 
 
21
        // GetPath returns the slash-separated relative path that this
 
22
        // entry represents.
 
23
        GetPath() string
 
24
 
 
25
        // Create causes the entry to be created, relative to basePath. It returns
 
26
        // a copy of the receiver.
 
27
        Create(c *gc.C, basePath string) Entry
 
28
 
 
29
        // Check checks that the entry exists, relative to basePath, and matches
 
30
        // the entry that would be created by Create. It returns a copy of the
 
31
        // receiver.
 
32
        Check(c *gc.C, basePath string) Entry
 
33
}
 
34
 
 
35
var (
 
36
        _ Entry = Dir{}
 
37
        _ Entry = File{}
 
38
        _ Entry = Symlink{}
 
39
        _ Entry = Removed{}
 
40
)
 
41
 
 
42
// Entries supplies convenience methods on Entry slices.
 
43
type Entries []Entry
 
44
 
 
45
// Paths returns the slash-separated path of every entry.
 
46
func (e Entries) Paths() []string {
 
47
        result := make([]string, len(e))
 
48
        for i, entry := range e {
 
49
                result[i] = entry.GetPath()
 
50
        }
 
51
        return result
 
52
}
 
53
 
 
54
// Create creates every entry relative to basePath and returns a copy of itself.
 
55
func (e Entries) Create(c *gc.C, basePath string) Entries {
 
56
        result := make([]Entry, len(e))
 
57
        for i, entry := range e {
 
58
                result[i] = entry.Create(c, basePath)
 
59
        }
 
60
        return result
 
61
}
 
62
 
 
63
// Check checks every entry relative to basePath and returns a copy of itself.
 
64
func (e Entries) Check(c *gc.C, basePath string) Entries {
 
65
        result := make([]Entry, len(e))
 
66
        for i, entry := range e {
 
67
                result[i] = entry.Check(c, basePath)
 
68
        }
 
69
        return result
 
70
}
 
71
 
 
72
// AsRemoveds returns a slice of Removed entries whose paths correspond to
 
73
// those in e.
 
74
func (e Entries) AsRemoveds() Entries {
 
75
        result := make([]Entry, len(e))
 
76
        for i, entry := range e {
 
77
                result[i] = Removed{entry.GetPath()}
 
78
        }
 
79
        return result
 
80
}
 
81
 
 
82
// join joins a slash-separated path to a filesystem basePath.
 
83
func join(basePath, path string) string {
 
84
        return filepath.Join(basePath, filepath.FromSlash(path))
 
85
}
 
86
 
 
87
// Dir is an Entry that allows directories to be created and verified. The
 
88
// Path field should use "/" as the path separator.
 
89
type Dir struct {
 
90
        Path string
 
91
        Perm os.FileMode
 
92
}
 
93
 
 
94
func (d Dir) GetPath() string {
 
95
        return d.Path
 
96
}
 
97
 
 
98
func (d Dir) Create(c *gc.C, basePath string) Entry {
 
99
        path := join(basePath, d.Path)
 
100
        err := os.MkdirAll(path, d.Perm)
 
101
        c.Assert(err, gc.IsNil)
 
102
        err = os.Chmod(path, d.Perm)
 
103
        c.Assert(err, gc.IsNil)
 
104
        return d
 
105
}
 
106
 
 
107
func (d Dir) Check(c *gc.C, basePath string) Entry {
 
108
        fileInfo, err := os.Lstat(join(basePath, d.Path))
 
109
        if !c.Check(err, gc.IsNil) {
 
110
                return d
 
111
        }
 
112
        c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.Perm)
 
113
        c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir)
 
114
        return d
 
115
}
 
116
 
 
117
// File is an Entry that allows plain files to be created and verified. The
 
118
// Path field should use "/" as the path separator.
 
119
type File struct {
 
120
        Path string
 
121
        Data string
 
122
        Perm os.FileMode
 
123
}
 
124
 
 
125
func (f File) GetPath() string {
 
126
        return f.Path
 
127
}
 
128
 
 
129
func (f File) Create(c *gc.C, basePath string) Entry {
 
130
        err := ioutil.WriteFile(join(basePath, f.Path), []byte(f.Data), f.Perm)
 
131
        c.Assert(err, gc.IsNil)
 
132
        return f
 
133
}
 
134
 
 
135
func (f File) Check(c *gc.C, basePath string) Entry {
 
136
        path := join(basePath, f.Path)
 
137
        fileInfo, err := os.Lstat(path)
 
138
        if !c.Check(err, gc.IsNil) {
 
139
                return f
 
140
        }
 
141
        mode := fileInfo.Mode()
 
142
        c.Check(mode&os.ModeType, gc.Equals, os.FileMode(0))
 
143
        c.Check(mode&os.ModePerm, gc.Equals, f.Perm)
 
144
        data, err := ioutil.ReadFile(path)
 
145
        c.Check(err, gc.IsNil)
 
146
        c.Check(string(data), gc.Equals, f.Data)
 
147
        return f
 
148
}
 
149
 
 
150
// Symlink is an Entry that allows symlinks to be created and verified. The
 
151
// Path field should use "/" as the path separator.
 
152
type Symlink struct {
 
153
        Path string
 
154
        Link string
 
155
}
 
156
 
 
157
func (s Symlink) GetPath() string {
 
158
        return s.Path
 
159
}
 
160
 
 
161
func (s Symlink) Create(c *gc.C, basePath string) Entry {
 
162
        err := os.Symlink(s.Link, join(basePath, s.Path))
 
163
        c.Assert(err, gc.IsNil)
 
164
        return s
 
165
}
 
166
 
 
167
func (s Symlink) Check(c *gc.C, basePath string) Entry {
 
168
        link, err := os.Readlink(join(basePath, s.Path))
 
169
        c.Check(err, gc.IsNil)
 
170
        c.Check(link, gc.Equals, s.Link)
 
171
        return s
 
172
}
 
173
 
 
174
// Removed is an Entry that indicates the absence of any entry. The Path
 
175
// field should use "/" as the path separator.
 
176
type Removed struct {
 
177
        Path string
 
178
}
 
179
 
 
180
func (r Removed) GetPath() string {
 
181
        return r.Path
 
182
}
 
183
 
 
184
func (r Removed) Create(c *gc.C, basePath string) Entry {
 
185
        err := os.RemoveAll(join(basePath, r.Path))
 
186
        c.Assert(err, gc.IsNil)
 
187
        return r
 
188
}
 
189
 
 
190
func (r Removed) Check(c *gc.C, basePath string) Entry {
 
191
        _, err := os.Lstat(join(basePath, r.Path))
 
192
        // utils.IsNotExist allows us to handle the following case:
 
193
        //  File{"foo", ...}.Create(...)
 
194
        //  Removed{"foo/bar"}.Check(...)
 
195
        // ...where os.IsNotExist would not work.
 
196
        c.Assert(err, jc.Satisfies, utils.IsNotExist)
 
197
        return r
 
198
}