state: make storage persistent by default #6795

Merged
merged 1 commit into from Jan 18, 2017

Conversation

Projects
None yet
4 participants
Member

axw commented Jan 13, 2017

Volumes and filesystems will not default to
being bound to the model, or machine, depending
on whether or not the storage can be persistent.

This is the minimal change. Volume-backed
filesystems are currently always considered
machine-scoped, which leads to undesirable
behaviour: the filesystem cannot be detached
from the machine it is initially attached to
(or doing so will cause the volume to be
destroyed). We will need to make further
changes so that volume-backed filesystems
are not scoped or bound to a single machine.

Member

axw commented Jan 13, 2017

QA

(combined with #6673)

  1. juju bootstrap google
  2. juju deploy cs:~prometheus-charmers/prometheus --storage metrics-filesystem=gce,10G
  3. try to remove volume related to prometheus storage, get an error saying that it contains a filesystem
  4. try to remove filesystem related to prometheus storage, get an error saying that it has assigned storage
  5. remove storage from prometheus (successful), leaves both volume and filesystem behind
  6. remove filesystem (successful), also detaches/destroys/removes volume
Member

anastasiamac commented Jan 15, 2017

What are doing when we are removing units/application with persistent storage? Do we need to ask user confirmation whether persistent storage needs to be removed? Do we default to removing it unless there is an explicit "get rid of persistent storage" option set?

Member

axw commented Jan 16, 2017

What are doing when we are removing units/application with persistent storage? Do we need to ask user confirmation whether persistent storage needs to be removed? Do we default to removing it unless there is an explicit "get rid of persistent storage" option set?

When this change goes ahead, we'll only remove the storage when (a) the user explicitly says so (runs a remove- command), or (b) when the model is destroyed (or machine, for inherently machine-bound storage, such as loop, tmpfs, etc.).

We may want to extend the remove-application/remove-unit/remove-machine commands later to give the user a chance to make a choice up front; that can be layered on top of this change.

Looks good

+ for _, v := range volumes {
+ err := st.DestroyVolume(v.VolumeTag())
+ if err != nil && !IsContainsFilesystem(err) {
+ return errors.Trace(err)
@wallyworld

wallyworld Jan 17, 2017

Owner

I wonder if this error should be typed so that we can avoid log noise. Wouldn't it be the case that the filesystem cleanup would take care of the underlying volume? So a dying model should see both filesystems and volumes destroyed as expected.

@axw

axw Jan 18, 2017

Member

I don't follow. DestroyVolume returns a typed error if the volume contains a filesystem. If you destroy the filesystem, and the volume is bound to the filesystem, the volume will be destroyed when the filesystem is removed from the model.

@wallyworld

wallyworld Jan 18, 2017

Owner

Sorry, I misread errors.Trace() and errors.Errorf() for some reason.

state/cleanup.go
+ return nil
+}
+
+// cleanupApplicationsForDyingModel sets all services to Dying, if they are
@wallyworld

wallyworld Jan 17, 2017

Owner

can we drive-by change services to application here?

@axw

axw Jan 18, 2017

Member

Done

buildTxn := func(attempt int) ([]txn.Op, error) {
filesystem, err := st.filesystemByTag(tag)
if errors.IsNotFound(err) {
return nil, jujutxn.ErrNoOperations
} else if err != nil {
return nil, errors.Trace(err)
}
+ if filesystem.doc.StorageId != "" {
+ return nil, errors.Errorf(
+ "filesystem is assigned to %s",
@wallyworld

wallyworld Jan 17, 2017

Owner

it would be useful to include the machine/unit in the error message as that's how users tend think of the scope of storage

@axw

axw Jan 18, 2017

Member

A filesystem is not necessarily attached to a machine, and is never directly associated with a unit. The associated storage could potentially be attached to multiple units. I'm going to leave this for now, unless you have strong feelings about it.

@wallyworld

wallyworld Jan 18, 2017

Owner

Let's release as is and get feedback. Maybe just add an extra line to the error message telling the user to juju show-storage to get more details on what in the model is using the storage. An average user will have no idea what "assigned to " means.

state/filesystem.go
- Count: 1,
+func (st *State) filesystemParamsWithDefaults(params FilesystemParams, machineId string) (FilesystemParams, error) {
+ if params.Pool == "" {
+ envConfig, err := st.ModelConfig()
@wallyworld

wallyworld Jan 17, 2017

Owner

nit: modelConfig

@axw

axw Jan 18, 2017

Member

Fixed.

+ filesystem := s.storageInstanceFilesystem(c, storageTag)
+
+ err = s.State.DestroyFilesystem(filesystem.FilesystemTag())
+ c.Assert(err, gc.ErrorMatches, "destroying filesystem 0/0: filesystem is assigned to storage data/0")
@wallyworld

wallyworld Jan 17, 2017

Owner

As per earlier comment, including machine/unit would be helpful here for the user

state/filesystem_test.go
+ c.Assert(err, gc.ErrorMatches, "destroying filesystem 0/0: filesystem is assigned to storage data/0")
+
+ removeStorageInstance(c, s.State, storageTag)
+ err = s.State.DestroyFilesystem(filesystem.FilesystemTag())
@wallyworld

wallyworld Jan 17, 2017

Owner

can we replace these 2 lines with assertDestroy() to do a proper check

@axw

axw Jan 18, 2017

Member

I don't understand. There is no assertDestroy function.

@wallyworld

wallyworld Jan 18, 2017

Owner

Ah, hard to tell from the diff. There's a local func defined on line 526. This destroys and checks life. Perhaps that func could be extracted to live on the test struct and used in multiple tests.

@axw

axw Jan 18, 2017

Member

I see. That one exists for testing concurrency. Probably not worth extracting, but I'll look at doing the same thing here.

@axw

axw Jan 18, 2017

Member

Hmm actually no, doesn't really make sense here, since you can't yet assign a filesystem to a storage instance post-creation. If you were able to do that, we could do a concurrent DestroyFilesystem and assign-to-storage.

I've extracted the DestroyFilesystem & life check, and used it throughout the suite.

@@ -95,6 +95,12 @@ type modelEntityRefsDoc struct {
// Applicatons contains the names of the applications in the model.
Applications []string `bson:"applications"`
+
+ // Volumes contains the IDs of the volumes in the model.
@wallyworld

wallyworld Jan 17, 2017

Owner

perhaps s/in the model/bound to the model
to be consistent with terminology used elsewhere?

@axw

axw Jan 18, 2017

Member

They mean different things. A volume "in" the model means that it is tracked by the model. If it is "bound to" the model, it means that it dies when the model dies.

@wallyworld

wallyworld Jan 18, 2017

Owner

Ok, subtle.

state/model_test.go
@@ -661,6 +661,81 @@ func (s *ModelSuite) TestProcessDyingEnvironWithMachinesAndServicesNoOp(c *gc.C)
c.Assert(env.Destroy(), jc.ErrorIsNil)
}
+func (s *ModelSuite) TestProcessDyingEnvironWithVolumeBackedFilesystems(c *gc.C) {
@wallyworld

wallyworld Jan 17, 2017

Owner

DyingModel

@axw

axw Jan 18, 2017

Member

Fixed.

state/model_test.go
+ c.Assert(err, jc.ErrorIsNil)
+ c.Assert(machine.EnsureDead(), jc.ErrorIsNil)
+ c.Assert(machine.Remove(), jc.ErrorIsNil)
+ err = st.ProcessDyingModel()
@wallyworld

wallyworld Jan 17, 2017

Owner

can we add a comment similar to the previous test about volume being persistent

@wallyworld

wallyworld Jan 17, 2017

Owner

doesn't there need to be tests for the non-persistent cases also?

@axw

axw Jan 18, 2017

Member

The only way you can have non-persistent storage is by having it bound to some non-model entity. There are lifecycle binding tests elsewhere.

@axw

axw Jan 18, 2017

Member

(Also: done, added the comment)

@@ -655,6 +667,12 @@ func (st *State) DestroyVolume(tag names.VolumeTag) (err error) {
} else if err != nil {
return nil, errors.Trace(err)
}
+ if volume.doc.StorageId != "" {
+ return nil, errors.Errorf(
+ "volume is assigned to %s",
@wallyworld

wallyworld Jan 17, 2017

Owner

include machine/unit here as well

state/volume.go
- Count: 1,
+func (st *State) volumeParamsWithDefaults(params VolumeParams, machineId string) (VolumeParams, error) {
+ if params.Pool == "" {
+ envConfig, err := st.ModelConfig()
@wallyworld

wallyworld Jan 17, 2017

Owner

nit: modelConfig

@axw

axw Jan 18, 2017

Member

done

+ c.Assert(err, gc.ErrorMatches, "destroying volume 0/0: volume is assigned to storage data/0")
+
+ removeStorageInstance(c, s.State, storageTag)
+ err = s.State.DestroyVolume(volume.VolumeTag())
@wallyworld

wallyworld Jan 17, 2017

Owner

replace these 2 lines by assertDestroy()

state: make storage persistent by default
Volumes and filesystems will not default to
being bound to the model, or machine, depending
on whether or not the storage can be persistent.

This is the minimal change. Volume-backed
filesystems are currently always considered
machine-scoped, which leads to undesirable
behaviour: the filesystem cannot be detached
from the machine it is initially attached to
(or doing so will cause the volume to be
destroyed). We will need to make further
changes so that volume-backed filesystems
are not scoped or bound to a single machine.
Member

axw commented Jan 18, 2017

$$merge$$

Contributor

jujubot commented Jan 18, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

@jujubot jujubot merged commit 666c913 into juju:feature-persistent-storage Jan 18, 2017

1 check failed

github-check-merge-juju Built PR, ran unit tests, and tested LXD deploy. Use !!.*!! to request another build. IE, !!build!!, !!retry!!
Details

jujubot added a commit that referenced this pull request Feb 23, 2017

Merge pull request #7015 from axw/state-storage-model-2.2
state: prepare for persistent storage

## Description of change

We make various changes to the state package
to allow for volumes and filesystems to be
bound to a model, rather than only machine
or storage instance. We continue to bind
the storage to storage instances; we will
later flip the switch when commands for
detaching, attaching, and removing storage
have been introduced.

This is the minimal change. Volume-backed
filesystems are currently always considered
machine-scoped, which leads to undesirable
behaviour: the filesystem cannot be detached
from the machine it is initially attached to
(or doing so will cause the volume to be
destroyed). We will need to make further
changes so that volume-backed filesystems
are not scoped or bound to a single machine.

This is based on #6795,
with a couple of small fixes to the assertions; and
until we have commands for destroying storage, we
continue to bind machine storage to the storage
instance.

## QA steps

1. juju bootstrap localhost
2. juju deploy a charm with rootfs storage
3. juju remove-unit
4. ensure the machine storage is also removed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment