1+ //
2+ // DISCLAIMER
3+ //
4+ // Copyright 2020 ArangoDB GmbH, Cologne, Germany
5+ //
6+ // Licensed under the Apache License, Version 2.0 (the "License");
7+ // you may not use this file except in compliance with the License.
8+ // You may obtain a copy of the License at
9+ //
10+ // http://www.apache.org/licenses/LICENSE-2.0
11+ //
12+ // Unless required by applicable law or agreed to in writing, software
13+ // distributed under the License is distributed on an "AS IS" BASIS,
14+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+ // See the License for the specific language governing permissions and
16+ // limitations under the License.
17+ //
18+ // Copyright holder is ArangoDB GmbH, Cologne, Germany
19+ //
20+ // Author Adam Janikowski
21+ //
22+
23+ package reconcile
24+
25+ import (
26+ "github.com/rs/zerolog"
27+ core "k8s.io/api/core/v1"
28+
29+ api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
30+ "github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
31+ )
32+
33+ // createRotateServerStoragePlan creates plan to rotate a server and its volume because of a
34+ // different storage class or a difference in storage resource requirements.
35+ func createRotateServerSecurityPlan (log zerolog.Logger , spec api.DeploymentSpec , status api.DeploymentStatus ,
36+ pods []core.Pod ) api.Plan {
37+ var plan api.Plan
38+ status .Members .ForeachServerGroup (func (group api.ServerGroup , members api.MemberStatusList ) error {
39+ for _ , m := range members {
40+ if ! plan .IsEmpty () {
41+ // Only 1 change at a time
42+ continue
43+ }
44+
45+ groupSpec := spec .GetServerGroupSpec (group )
46+
47+ pod , found := k8sutil .GetPodByName (pods , m .PodName )
48+ if ! found {
49+ continue
50+ }
51+
52+ container , ok := getServerContainer (pod .Spec .Containers )
53+ if ! ok {
54+ // We do not have server container in pod, which is not desired
55+ continue
56+ }
57+
58+ groupSC := groupSpec .SecurityContext .NewSecurityContext ()
59+ containerSC := container .SecurityContext
60+
61+ if ! compareSC (groupSC , containerSC ) {
62+ log .Info ().Str ("member" , m .ID ).Str ("group" , group .AsRole ()).Msg ("Rotating security context" )
63+ plan = append (plan ,
64+ api .NewAction (api .ActionTypeRotateMember , group , m .ID ),
65+ api .NewAction (api .ActionTypeWaitForMemberUp , group , m .ID ),
66+ )
67+ }
68+ }
69+ return nil
70+ })
71+ return plan
72+ }
73+
74+ func getServerContainer (containers []core.Container ) (core.Container , bool ) {
75+ for _ , container := range containers {
76+ if container .Name == k8sutil .ServerContainerName {
77+ return container , true
78+ }
79+ }
80+
81+ return core.Container {}, false
82+ }
83+
84+ func compareSC (a ,b * core.SecurityContext ) bool {
85+ if a == nil && b == nil {
86+ return true
87+ }
88+
89+ if a == nil || b == nil {
90+ return false
91+ }
92+
93+ if ok := compareCapabilities (a .Capabilities , b .Capabilities ); ! ok {
94+ return false
95+ }
96+
97+ return true
98+ }
99+
100+ func compareCapabilities (a ,b * core.Capabilities ) bool {
101+ if a == nil && b == nil {
102+ return true
103+ }
104+
105+ if a == nil || b == nil {
106+ return false
107+ }
108+
109+ if ok := compareCapabilityLists (a .Add , b .Add ); ! ok {
110+ return false
111+ }
112+
113+ if ok := compareCapabilityLists (a .Drop , b .Drop ); ! ok {
114+ return false
115+ }
116+
117+ return true
118+ }
119+
120+ func compareCapabilityLists (a , b []core.Capability ) bool {
121+ if a == nil && b == nil {
122+ return true
123+ }
124+
125+ if a == nil || b == nil {
126+ return false
127+ }
128+
129+ if len (a ) != len (b ) {
130+ return false
131+ }
132+
133+ checked := map [core.Capability ]bool {}
134+
135+ for _ , capability := range a {
136+ checked [capability ] = false
137+ }
138+
139+ for _ , capability := range b {
140+ if _ , ok := checked [capability ]; ! ok {
141+ return false
142+ }
143+
144+ checked [capability ] = true
145+ }
146+
147+ for _ , check := range checked {
148+ if ! check {
149+ return false
150+ }
151+ }
152+
153+ return true
154+ }
0 commit comments