forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
controller.go
149 lines (128 loc) · 4.06 KB
/
controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package controller
import (
"fmt"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"github.com/openshift/origin/pkg/security"
"github.com/openshift/origin/pkg/security/mcs"
"github.com/openshift/origin/pkg/security/uid"
"github.com/openshift/origin/pkg/security/uidallocator"
)
type MCSAllocationFunc func(uid.Block) *mcs.Label
// DefaultMCSAllocation returns a label from the MCS range that matches the offset
// within the overall range. blockSize must be a positive integer representing the
// number of labels to jump past in the category space (if 1, range == label, if 2
// each range will have two labels).
func DefaultMCSAllocation(from *uid.Range, to *mcs.Range, blockSize int) MCSAllocationFunc {
return func(block uid.Block) *mcs.Label {
ok, offset := from.Offset(block)
if !ok {
return nil
}
if blockSize > 0 {
offset = offset * uint32(blockSize)
}
label, _ := to.LabelAt(uint64(offset))
return label
}
}
type Allocation struct {
uid uidallocator.Interface
mcs MCSAllocationFunc
client kcoreclient.NamespaceInterface
}
// retryCount is the number of times to retry on a conflict when updating a namespace
const retryCount = 2
// Next processes a changed namespace and tries to allocate a uid range for it. If it is
// successful, an mcs label corresponding to the relative position of the range is also
// set.
func (c *Allocation) Next(ns *kapi.Namespace) error {
tx := &tx{}
defer tx.Rollback()
if _, ok := ns.Annotations[security.UIDRangeAnnotation]; ok {
return nil
}
if ns.Annotations == nil {
ns.Annotations = make(map[string]string)
}
// do uid allocation
block, err := c.uid.AllocateNext()
if err != nil {
return err
}
tx.Add(func() error { return c.uid.Release(block) })
ns.Annotations[security.UIDRangeAnnotation] = block.String()
ns.Annotations[security.SupplementalGroupsAnnotation] = block.String()
if _, ok := ns.Annotations[security.MCSAnnotation]; !ok {
if label := c.mcs(block); label != nil {
ns.Annotations[security.MCSAnnotation] = label.String()
}
}
// TODO: could use a client.GuaranteedUpdate/Merge function
for i := 0; i < retryCount; i++ {
_, err := c.client.Update(ns)
if err == nil {
// commit and exit
tx.Commit()
return nil
}
if errors.IsNotFound(err) {
return nil
}
if !errors.IsConflict(err) {
return err
}
newNs, err := c.client.Get(ns.Name)
if errors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}
if changedAndSetAnnotations(ns, newNs) {
return nil
}
// try again
if newNs.Annotations == nil {
newNs.Annotations = make(map[string]string)
}
newNs.Annotations[security.UIDRangeAnnotation] = ns.Annotations[security.UIDRangeAnnotation]
newNs.Annotations[security.SupplementalGroupsAnnotation] = ns.Annotations[security.SupplementalGroupsAnnotation]
newNs.Annotations[security.MCSAnnotation] = ns.Annotations[security.MCSAnnotation]
ns = newNs
}
return fmt.Errorf("unable to allocate security info on %q after %d retries", ns.Name, retryCount)
}
func changedAndSetAnnotations(old, ns *kapi.Namespace) bool {
if value, ok := ns.Annotations[security.UIDRangeAnnotation]; ok && value != old.Annotations[security.UIDRangeAnnotation] {
return true
}
if value, ok := ns.Annotations[security.MCSAnnotation]; ok && value != old.Annotations[security.MCSAnnotation] {
return true
}
if value, ok := ns.Annotations[security.SupplementalGroupsAnnotation]; ok && value != old.Annotations[security.SupplementalGroupsAnnotation] {
return true
}
return false
}
type tx struct {
rollback []func() error
}
func (tx *tx) Add(fn func() error) {
tx.rollback = append(tx.rollback, fn)
}
func (tx *tx) HasChanges() bool {
return len(tx.rollback) > 0
}
func (tx *tx) Rollback() {
for _, fn := range tx.rollback {
if err := fn(); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to undo tx: %v", err))
}
}
}
func (tx *tx) Commit() {
tx.rollback = nil
}