1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
13
// StagedResource represents resource info that has been added to the
14
// "staging" area of the underlying data store. It remains unavailable
15
// until finalized, at which point it moves out of the staging area and
16
// replaces the current active resource info.
17
type StagedResource struct {
18
base ResourcePersistenceBase
23
func (staged StagedResource) stage() error {
24
buildTxn := func(attempt int) ([]txn.Op, error) {
28
ops = newInsertStagedResourceOps(staged.stored)
30
ops = newEnsureStagedResourceSameOps(staged.stored)
32
return nil, errors.NewAlreadyExists(nil, "already staged")
34
if staged.stored.PendingID == "" {
35
// Only non-pending resources must have an existing service.
36
ops = append(ops, staged.base.ApplicationExistsOps(staged.stored.ApplicationID)...)
41
if err := staged.base.Run(buildTxn); err != nil {
42
return errors.Trace(err)
47
// Unstage ensures that the resource is removed
48
// from the staging area. If it isn't in the staging area
49
// then this is a noop.
50
func (staged StagedResource) Unstage() error {
51
buildTxn := func(attempt int) ([]txn.Op, error) {
53
// The op has no assert so we should not get here.
54
return nil, errors.New("unstaging the resource failed")
57
ops := newRemoveStagedResourceOps(staged.id)
60
if err := staged.base.Run(buildTxn); err != nil {
61
return errors.Trace(err)
66
// Activate makes the staged resource the active resource.
67
func (staged StagedResource) Activate() error {
68
buildTxn := func(attempt int) ([]txn.Op, error) {
69
// This is an "upsert".
73
ops = newInsertResourceOps(staged.stored)
75
ops = newUpdateResourceOps(staged.stored)
77
return nil, errors.New("setting the resource failed")
79
if staged.stored.PendingID == "" {
80
// Only non-pending resources must have an existing service.
81
ops = append(ops, staged.base.ApplicationExistsOps(staged.stored.ApplicationID)...)
83
// No matter what, we always remove any staging.
84
ops = append(ops, newRemoveStagedResourceOps(staged.id)...)
86
// If we are changing the bytes for a resource, we increment the
87
// CharmModifiedVersion on the service, since resources are integral to
88
// the high level "version" of the charm.
89
if staged.stored.PendingID == "" {
90
hasNewBytes, err := staged.hasNewBytes()
92
logger.Errorf("can't read existing resource during activate: %v", errors.Details(err))
93
return nil, errors.Trace(err)
96
incOps := staged.base.IncCharmModifiedVersionOps(staged.stored.ApplicationID)
97
ops = append(ops, incOps...)
100
logger.Debugf("activate ops: %#v", ops)
103
if err := staged.base.Run(buildTxn); err != nil {
104
return errors.Trace(err)
109
func (staged StagedResource) hasNewBytes() (bool, error) {
110
var current resourceDoc
111
err := staged.base.One(resourcesC, staged.stored.ID, ¤t)
113
case errors.IsNotFound(err):
114
// if there's no current resource stored, then any non-zero bytes will
116
return !staged.stored.Fingerprint.IsZero(), nil
118
return false, errors.Annotate(err, "couldn't read existing resource")
120
diff := !bytes.Equal(staged.stored.Fingerprint.Bytes(), current.Fingerprint)