forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
repair.go
103 lines (90 loc) · 3.4 KB
/
repair.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
package controller
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
utilwait "k8s.io/apimachinery/pkg/util/wait"
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
"k8s.io/kubernetes/pkg/registry/core/rangeallocation"
"github.com/openshift/origin/pkg/security"
"github.com/openshift/origin/pkg/security/uid"
"github.com/openshift/origin/pkg/security/uidallocator"
)
// Repair is a controller loop that periodically examines all UID allocations
// and logs any errors, and then sets the compacted and accurate list of both
//
// Can be run at infrequent intervals, and is best performed on startup of the master.
// Is level driven and idempotent - all claimed UIDs will be updated into the allocator
// map at the end of a single execution loop if no race is encountered.
//
type Repair struct {
interval time.Duration
client kcoreclient.NamespaceInterface
alloc rangeallocation.RangeRegistry
uidRange *uid.Range
}
// NewRepair creates a controller that periodically ensures that all UIDs labels that are allocated in the cluster
// are claimed.
func NewRepair(interval time.Duration, client kcoreclient.NamespaceInterface, uidRange *uid.Range, alloc rangeallocation.RangeRegistry) *Repair {
return &Repair{
interval: interval,
client: client,
uidRange: uidRange,
alloc: alloc,
}
}
// RunUntil starts the controller until the provided ch is closed.
func (c *Repair) RunUntil(ch chan struct{}) {
utilwait.Until(func() {
if err := c.RunOnce(); err != nil {
utilruntime.HandleError(err)
}
}, c.interval, ch)
}
// RunOnce verifies the state of allocations and returns an error if an unrecoverable problem occurs.
func (c *Repair) RunOnce() error {
// TODO: (per smarterclayton) if Get() or List() is a weak consistency read,
// or if they are executed against different leaders,
// the ordering guarantee required to ensure no item is allocated twice is violated.
// List must return a ResourceVersion higher than the etcd index Get,
// and the release code must not release items that have allocated but not yet been created
// See #8295
latest, err := c.alloc.Get()
if err != nil {
return fmt.Errorf("unable to refresh the security allocation UID blocks: %v", err)
}
list, err := c.client.List(metav1.ListOptions{})
if err != nil {
return fmt.Errorf("unable to refresh the security allocation UID blocks: %v", err)
}
uids := uidallocator.NewInMemory(c.uidRange)
for _, ns := range list.Items {
value, ok := ns.Annotations[security.UIDRangeAnnotation]
if !ok {
continue
}
block, err := uid.ParseBlock(value)
if err != nil {
continue
}
switch err := uids.Allocate(block); err {
case nil:
case uidallocator.ErrNotInRange, uidallocator.ErrAllocated:
continue
case uidallocator.ErrFull:
// TODO: send event
return fmt.Errorf("the UID range %s is full; you must widen the range in order to allocate more UIDs", c.uidRange)
default:
return fmt.Errorf("unable to allocate UID block %s for namespace %s due to an unknown error, exiting: %v", block, ns.Name, err)
}
}
err = uids.Snapshot(latest)
if err != nil {
return fmt.Errorf("unable to persist the updated namespace UID allocations: %v", err)
}
if err := c.alloc.CreateOrUpdate(latest); err != nil {
return fmt.Errorf("unable to persist the updated namespace UID allocations: %v", err)
}
return nil
}