Skip to content

Commit 207eefd

Browse files
committed
apiserver/uniter: add (Watch)StorageAttachment
Extend the uniter API to provide a means of watching and querying a single storage attachment. The existing methods for watching all of a unit's storage attachments were renamed for clarity.
1 parent 81f61c0 commit 207eefd

File tree

8 files changed

+320
-35
lines changed

8 files changed

+320
-35
lines changed

api/uniter/storage.go

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ func NewStorageAccessor(facade base.FacadeCaller) *StorageAccessor {
2222
return &StorageAccessor{facade}
2323
}
2424

25-
// StorageAttachments returns the storage instances attached to a unit.
26-
func (sa *StorageAccessor) StorageAttachments(unitTag names.Tag) ([]params.StorageAttachment, error) {
25+
// UnitStorageAttachments returns the storage instances attached to a unit.
26+
func (sa *StorageAccessor) UnitStorageAttachments(unitTag names.UnitTag) ([]params.StorageAttachment, error) {
2727
if sa.facade.BestAPIVersion() < 2 {
28-
// StorageAttachments() was introduced in UniterAPIV2.
29-
return nil, errors.NotImplementedf("StorageAttachments() (need V2+)")
28+
// UnitStorageAttachments() was introduced in UniterAPIV2.
29+
return nil, errors.NotImplementedf("UnitStorageAttachments() (need V2+)")
3030
}
3131
args := params.Entities{
3232
Entities: []params.Entity{
3333
{Tag: unitTag.String()},
3434
},
3535
}
3636
var results params.StorageAttachmentsResults
37-
err := sa.facade.FacadeCall("StorageAttachments", args, &results)
37+
err := sa.facade.FacadeCall("UnitStorageAttachments", args, &results)
3838
if err != nil {
3939
return nil, errors.Trace(err)
4040
}
@@ -48,15 +48,15 @@ func (sa *StorageAccessor) StorageAttachments(unitTag names.Tag) ([]params.Stora
4848
return result.Result, nil
4949
}
5050

51-
// WatchStorageAttachments starts a watcher for changes to storage
51+
// WatchUnitStorageAttachments starts a watcher for changes to storage
5252
// attachments related to the unit. The watcher will return the
5353
// IDs of the corresponding storage instances.
54-
func (sa *StorageAccessor) WatchStorageAttachments(unitTag names.UnitTag) (watcher.StringsWatcher, error) {
54+
func (sa *StorageAccessor) WatchUnitStorageAttachments(unitTag names.UnitTag) (watcher.StringsWatcher, error) {
5555
var results params.StringsWatchResults
5656
args := params.Entities{
5757
Entities: []params.Entity{{Tag: unitTag.String()}},
5858
}
59-
err := sa.facade.FacadeCall("WatchStorageAttachments", args, &results)
59+
err := sa.facade.FacadeCall("WatchUnitStorageAttachments", args, &results)
6060
if err != nil {
6161
return nil, err
6262
}
@@ -70,3 +70,56 @@ func (sa *StorageAccessor) WatchStorageAttachments(unitTag names.UnitTag) (watch
7070
w := watcher.NewStringsWatcher(sa.facade.RawAPICaller(), result)
7171
return w, nil
7272
}
73+
74+
// StorageAttachment returns the storage attachment with the specified
75+
// unit and storage tags.
76+
func (sa *StorageAccessor) StorageAttachment(storageTag names.StorageTag, unitTag names.UnitTag) (params.StorageAttachment, error) {
77+
if sa.facade.BestAPIVersion() < 2 {
78+
// StorageAttachment() was introduced in UniterAPIV2.
79+
return params.StorageAttachment{}, errors.NotImplementedf("StorageAttachment() (need V2+)")
80+
}
81+
args := params.StorageAttachmentIds{
82+
Ids: []params.StorageAttachmentId{{
83+
StorageTag: storageTag.String(),
84+
UnitTag: unitTag.String(),
85+
}},
86+
}
87+
var results params.StorageAttachmentResults
88+
err := sa.facade.FacadeCall("StorageAttachments", args, &results)
89+
if err != nil {
90+
return params.StorageAttachment{}, errors.Trace(err)
91+
}
92+
if len(results.Results) != 1 {
93+
panic(errors.Errorf("expected 1 result, got %d", len(results.Results)))
94+
}
95+
result := results.Results[0]
96+
if result.Error != nil {
97+
return params.StorageAttachment{}, result.Error
98+
}
99+
return result.Result, nil
100+
}
101+
102+
// WatchStorageAttachments start a watcher for changes to the storage
103+
// attachment with the specified unit and storage tags.
104+
func (sa *StorageAccessor) WatchStorageAttachment(storageTag names.StorageTag, unitTag names.UnitTag) (watcher.NotifyWatcher, error) {
105+
var results params.NotifyWatchResults
106+
args := params.StorageAttachmentIds{
107+
Ids: []params.StorageAttachmentId{{
108+
StorageTag: storageTag.String(),
109+
UnitTag: unitTag.String(),
110+
}},
111+
}
112+
err := sa.facade.FacadeCall("WatchStorageAttachments", args, &results)
113+
if err != nil {
114+
return nil, err
115+
}
116+
if len(results.Results) != 1 {
117+
return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
118+
}
119+
result := results.Results[0]
120+
if result.Error != nil {
121+
return nil, result.Error
122+
}
123+
w := watcher.NewNotifyWatcher(sa.facade.RawAPICaller(), result)
124+
return w, nil
125+
}

api/uniter/storage_test.go

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type storageSuite struct {
2121
coretesting.BaseSuite
2222
}
2323

24-
func (s *storageSuite) TestStorageAttachments(c *gc.C) {
24+
func (s *storageSuite) TestUnitStorageAttachments(c *gc.C) {
2525
storageAttachments := []params.StorageAttachment{{
2626
StorageTag: "storage-whatever-0",
2727
OwnerTag: "service-mysql",
@@ -35,7 +35,7 @@ func (s *storageSuite) TestStorageAttachments(c *gc.C) {
3535
c.Check(objType, gc.Equals, "Uniter")
3636
c.Check(version, gc.Equals, 2)
3737
c.Check(id, gc.Equals, "")
38-
c.Check(request, gc.Equals, "StorageAttachments")
38+
c.Check(request, gc.Equals, "UnitStorageAttachments")
3939
c.Check(arg, gc.DeepEquals, params.Entities{
4040
Entities: []params.Entity{{Tag: "unit-mysql-0"}},
4141
})
@@ -50,7 +50,7 @@ func (s *storageSuite) TestStorageAttachments(c *gc.C) {
5050
})
5151

5252
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
53-
attachments, err := st.StorageAttachments(names.NewUnitTag("mysql/0"))
53+
attachments, err := st.UnitStorageAttachments(names.NewUnitTag("mysql/0"))
5454
c.Check(err, jc.ErrorIsNil)
5555
c.Check(called, jc.IsTrue)
5656
c.Assert(attachments, gc.DeepEquals, storageAttachments)
@@ -64,25 +64,27 @@ func (s *storageSuite) TestStorageAttachmentResultCountMismatch(c *gc.C) {
6464
return nil
6565
})
6666
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
67-
c.Assert(func() { st.StorageAttachments(names.NewUnitTag("mysql/0")) }, gc.PanicMatches, "expected 1 result, got 2")
67+
c.Assert(func() {
68+
st.UnitStorageAttachments(names.NewUnitTag("mysql/0"))
69+
}, gc.PanicMatches, "expected 1 result, got 2")
6870
}
6971

7072
func (s *storageSuite) TestAPIErrors(c *gc.C) {
7173
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
7274
return errors.New("bad")
7375
})
7476
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
75-
_, err := st.StorageAttachments(names.NewUnitTag("mysql/0"))
77+
_, err := st.UnitStorageAttachments(names.NewUnitTag("mysql/0"))
7678
c.Check(err, gc.ErrorMatches, "bad")
7779
}
7880

79-
func (s *storageSuite) TestWatchStorageAttachments(c *gc.C) {
81+
func (s *storageSuite) TestWatchUnitStorageAttachments(c *gc.C) {
8082
var called bool
8183
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
8284
c.Check(objType, gc.Equals, "Uniter")
8385
c.Check(version, gc.Equals, 2)
8486
c.Check(id, gc.Equals, "")
85-
c.Check(request, gc.Equals, "WatchStorageAttachments")
87+
c.Check(request, gc.Equals, "WatchUnitStorageAttachments")
8688
c.Check(arg, gc.DeepEquals, params.Entities{
8789
Entities: []params.Entity{{Tag: "unit-mysql-0"}},
8890
})
@@ -97,7 +99,74 @@ func (s *storageSuite) TestWatchStorageAttachments(c *gc.C) {
9799
})
98100

99101
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
100-
_, err := st.WatchStorageAttachments(names.NewUnitTag("mysql/0"))
102+
_, err := st.WatchUnitStorageAttachments(names.NewUnitTag("mysql/0"))
103+
c.Check(err, gc.ErrorMatches, "FAIL")
104+
c.Check(called, jc.IsTrue)
105+
}
106+
107+
func (s *storageSuite) TestWatchStorageAttachments(c *gc.C) {
108+
var called bool
109+
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
110+
c.Check(objType, gc.Equals, "Uniter")
111+
c.Check(version, gc.Equals, 2)
112+
c.Check(id, gc.Equals, "")
113+
c.Check(request, gc.Equals, "WatchStorageAttachments")
114+
c.Check(arg, gc.DeepEquals, params.StorageAttachmentIds{
115+
Ids: []params.StorageAttachmentId{{
116+
StorageTag: "storage-data-0",
117+
UnitTag: "unit-mysql-0",
118+
}},
119+
})
120+
c.Assert(result, gc.FitsTypeOf, &params.NotifyWatchResults{})
121+
*(result.(*params.NotifyWatchResults)) = params.NotifyWatchResults{
122+
Results: []params.NotifyWatchResult{{
123+
Error: &params.Error{Message: "FAIL"},
124+
}},
125+
}
126+
called = true
127+
return nil
128+
})
129+
130+
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
131+
_, err := st.WatchStorageAttachment(names.NewStorageTag("data/0"), names.NewUnitTag("mysql/0"))
101132
c.Check(err, gc.ErrorMatches, "FAIL")
102133
c.Check(called, jc.IsTrue)
103134
}
135+
136+
func (s *storageSuite) TestStorageAttachments(c *gc.C) {
137+
storageAttachment := params.StorageAttachment{
138+
StorageTag: "storage-whatever-0",
139+
OwnerTag: "service-mysql",
140+
UnitTag: "unit-mysql-0",
141+
Kind: params.StorageKindBlock,
142+
Location: "/dev/sda",
143+
}
144+
145+
var called bool
146+
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
147+
c.Check(objType, gc.Equals, "Uniter")
148+
c.Check(version, gc.Equals, 2)
149+
c.Check(id, gc.Equals, "")
150+
c.Check(request, gc.Equals, "StorageAttachments")
151+
c.Check(arg, gc.DeepEquals, params.StorageAttachmentIds{
152+
Ids: []params.StorageAttachmentId{{
153+
StorageTag: "storage-data-0",
154+
UnitTag: "unit-mysql-0",
155+
}},
156+
})
157+
c.Assert(result, gc.FitsTypeOf, &params.StorageAttachmentResults{})
158+
*(result.(*params.StorageAttachmentResults)) = params.StorageAttachmentResults{
159+
Results: []params.StorageAttachmentResult{{
160+
Result: storageAttachment,
161+
}},
162+
}
163+
called = true
164+
return nil
165+
})
166+
167+
st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
168+
attachment, err := st.StorageAttachment(names.NewStorageTag("data/0"), names.NewUnitTag("mysql/0"))
169+
c.Check(err, jc.ErrorIsNil)
170+
c.Check(called, jc.IsTrue)
171+
c.Assert(attachment, gc.DeepEquals, storageAttachment)
172+
}

api/uniter/unit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,5 +674,5 @@ func (u *Unit) WatchMeterStatus() (watcher.NotifyWatcher, error) {
674674
// WatchStorage returns a watcher for observing changes to the
675675
// unit's storage attachments.
676676
func (u *Unit) WatchStorage() (watcher.StringsWatcher, error) {
677-
return u.st.WatchStorageAttachments(u.tag)
677+
return u.st.WatchUnitStorageAttachments(u.tag)
678678
}

apiserver/params/storage.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,19 @@ type StorageAttachment struct {
8181

8282
Kind StorageKind
8383
Location string
84+
Life Life
85+
}
86+
87+
// StorageAttachmentId identifies a storage attachment by the tags of the
88+
// related unit and storage instance.
89+
type StorageAttachmentId struct {
90+
StorageTag string `json:"storagetag"`
91+
UnitTag string `json:"unittag"`
92+
}
93+
94+
// StorageAttachmentIds holds a set of storage attachment identifiers.
95+
type StorageAttachmentIds struct {
96+
Ids []StorageAttachmentId `json:"ids"`
8497
}
8598

8699
// StorageAttachmentsResult holds the result of an API call to retrieve details
@@ -96,6 +109,19 @@ type StorageAttachmentsResults struct {
96109
Results []StorageAttachmentsResult `json:"results,omitempty"`
97110
}
98111

112+
// StorageAttachmentResult holds the result of an API call to retrieve details
113+
// of a storage attachment.
114+
type StorageAttachmentResult struct {
115+
Result StorageAttachment `json:"result"`
116+
Error *Error `json:"error,omitempty"`
117+
}
118+
119+
// StorageAttachmentResults holds the result of an API call to retrieve details
120+
// of multiple storage attachments.
121+
type StorageAttachmentResults struct {
122+
Results []StorageAttachmentResult `json:"results,omitempty"`
123+
}
124+
99125
// Volume describes a storage volume in the environment.
100126
type Volume struct {
101127
VolumeTag string `json:"volumetag"`

apiserver/uniter/state.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
type storageStateInterface interface {
1313
StorageInstance(names.StorageTag) (state.StorageInstance, error)
1414
StorageAttachments(names.UnitTag) ([]state.StorageAttachment, error)
15+
StorageAttachment(names.StorageTag, names.UnitTag) (state.StorageAttachment, error)
1516
Unit(name string) (*state.Unit, error)
1617
WatchStorageAttachments(names.UnitTag) state.StringsWatcher
18+
WatchStorageAttachment(names.StorageTag, names.UnitTag) state.NotifyWatcher
1719
}
1820

1921
type storageStateShim struct {

0 commit comments

Comments
 (0)