~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/resources_persistence_staged.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package state
 
5
 
 
6
import (
 
7
        "bytes"
 
8
 
 
9
        "github.com/juju/errors"
 
10
        "gopkg.in/mgo.v2/txn"
 
11
)
 
12
 
 
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
 
19
        id     string
 
20
        stored storedResource
 
21
}
 
22
 
 
23
func (staged StagedResource) stage() error {
 
24
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
25
                var ops []txn.Op
 
26
                switch attempt {
 
27
                case 0:
 
28
                        ops = newInsertStagedResourceOps(staged.stored)
 
29
                case 1:
 
30
                        ops = newEnsureStagedResourceSameOps(staged.stored)
 
31
                default:
 
32
                        return nil, errors.NewAlreadyExists(nil, "already staged")
 
33
                }
 
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)...)
 
37
                }
 
38
 
 
39
                return ops, nil
 
40
        }
 
41
        if err := staged.base.Run(buildTxn); err != nil {
 
42
                return errors.Trace(err)
 
43
        }
 
44
        return nil
 
45
}
 
46
 
 
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) {
 
52
                if attempt > 0 {
 
53
                        // The op has no assert so we should not get here.
 
54
                        return nil, errors.New("unstaging the resource failed")
 
55
                }
 
56
 
 
57
                ops := newRemoveStagedResourceOps(staged.id)
 
58
                return ops, nil
 
59
        }
 
60
        if err := staged.base.Run(buildTxn); err != nil {
 
61
                return errors.Trace(err)
 
62
        }
 
63
        return nil
 
64
}
 
65
 
 
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".
 
70
                var ops []txn.Op
 
71
                switch attempt {
 
72
                case 0:
 
73
                        ops = newInsertResourceOps(staged.stored)
 
74
                case 1:
 
75
                        ops = newUpdateResourceOps(staged.stored)
 
76
                default:
 
77
                        return nil, errors.New("setting the resource failed")
 
78
                }
 
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)...)
 
82
                }
 
83
                // No matter what, we always remove any staging.
 
84
                ops = append(ops, newRemoveStagedResourceOps(staged.id)...)
 
85
 
 
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()
 
91
                        if err != nil {
 
92
                                logger.Errorf("can't read existing resource during activate: %v", errors.Details(err))
 
93
                                return nil, errors.Trace(err)
 
94
                        }
 
95
                        if hasNewBytes {
 
96
                                incOps := staged.base.IncCharmModifiedVersionOps(staged.stored.ApplicationID)
 
97
                                ops = append(ops, incOps...)
 
98
                        }
 
99
                }
 
100
                logger.Debugf("activate ops: %#v", ops)
 
101
                return ops, nil
 
102
        }
 
103
        if err := staged.base.Run(buildTxn); err != nil {
 
104
                return errors.Trace(err)
 
105
        }
 
106
        return nil
 
107
}
 
108
 
 
109
func (staged StagedResource) hasNewBytes() (bool, error) {
 
110
        var current resourceDoc
 
111
        err := staged.base.One(resourcesC, staged.stored.ID, &current)
 
112
        switch {
 
113
        case errors.IsNotFound(err):
 
114
                // if there's no current resource stored, then any non-zero bytes will
 
115
                // be new.
 
116
                return !staged.stored.Fingerprint.IsZero(), nil
 
117
        case err != nil:
 
118
                return false, errors.Annotate(err, "couldn't read existing resource")
 
119
        default:
 
120
                diff := !bytes.Equal(staged.stored.Fingerprint.Bytes(), current.Fingerprint)
 
121
                return diff, nil
 
122
        }
 
123
}