Skip to content

Commit 4939634

Browse files
authored
[Bugfix] Fix action timeouts (#1112)
1 parent 92afe45 commit 4939634

File tree

103 files changed

+3668
-640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+3668
-640
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
44
- (Feature) Add new field to DeploymentReplicationStatus with details on DC2DC sync status
55
- (Feature) Early connections support
6+
- (Bugfix) Fix and document action timeouts
67

78
## [1.2.16](https://github.com/arangodb/kube-arangodb/tree/1.2.16) (2022-09-14)
89
- (Feature) Add ArangoDeployment ServerGroupStatus

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88

99
# ArangoDB Kubernetes Operator Generated Documentation
10-
- [ArangoDB Operator Metrics & Alerts](./generated/metrics/README.md)
10+
- [ArangoDB Operator Metrics & Alerts](./generated/metrics/README.md)
11+
- [ArangoDB Actions](./generated/actions.md)

docs/generated/actions.md

Lines changed: 160 additions & 0 deletions
Large diffs are not rendered by default.

internal/actions.go

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 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+
21+
package internal
22+
23+
import (
24+
_ "embed"
25+
"fmt"
26+
"os"
27+
"path"
28+
"sort"
29+
"strings"
30+
"text/template"
31+
"time"
32+
33+
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"sigs.k8s.io/yaml"
35+
36+
"github.com/arangodb/kube-arangodb/internal/md"
37+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
38+
)
39+
40+
//go:embed actions.yaml
41+
var actions []byte
42+
43+
//go:embed actions.tmpl
44+
var actionsMD []byte
45+
46+
//go:embed actions.go.tmpl
47+
var actionsGoTemplate []byte
48+
49+
//go:embed actions.register.go.tmpl
50+
var actionsRegisterGoTemplate []byte
51+
52+
//go:embed actions.register.test.go.tmpl
53+
var actionsRegisterTestGoTemplate []byte
54+
55+
type ActionsInput struct {
56+
DefaultTimeout meta.Duration `json:"default_timeout"`
57+
58+
Actions map[string]Action `json:"actions"`
59+
}
60+
61+
func (i ActionsInput) Keys() []string {
62+
z := make([]string, 0, len(i.Actions))
63+
64+
for k := range i.Actions {
65+
z = append(z, k)
66+
}
67+
68+
sort.Strings(z)
69+
70+
return z
71+
}
72+
73+
type Scopes struct {
74+
Normal, High, Resource bool
75+
}
76+
77+
func (s Scopes) String() string {
78+
q := make([]string, 0, 3)
79+
if s.High {
80+
q = append(q, "High")
81+
}
82+
if s.Normal {
83+
q = append(q, "Normal")
84+
}
85+
if s.Resource {
86+
q = append(q, "Resource")
87+
}
88+
89+
if len(q) > 2 {
90+
q = []string{
91+
strings.Join(q[0:len(q)-1], ", "),
92+
q[len(q)-1],
93+
}
94+
}
95+
96+
return strings.Join(q, " and ")
97+
}
98+
99+
func (i ActionsInput) Scopes() map[string]Scopes {
100+
r := map[string]Scopes{}
101+
for k, a := range i.Actions {
102+
r[k] = Scopes{
103+
Normal: a.InScope("normal"),
104+
High: a.InScope("high"),
105+
Resource: a.InScope("resource"),
106+
}
107+
}
108+
return r
109+
}
110+
111+
func (i ActionsInput) StartFailureGracePeriods() map[string]string {
112+
r := map[string]string{}
113+
for k, a := range i.Actions {
114+
if a.StartupFailureGracePeriod == nil {
115+
r[k] = ""
116+
} else {
117+
r[k] = fmt.Sprintf("%d * time.Second", a.StartupFailureGracePeriod.Duration/time.Second)
118+
}
119+
}
120+
return r
121+
}
122+
123+
func (i ActionsInput) HighestScopes() map[string]string {
124+
r := map[string]string{}
125+
for k, a := range i.Scopes() {
126+
if a.High {
127+
r[k] = "High"
128+
} else if a.Normal {
129+
r[k] = "Normal"
130+
} else if a.Resource {
131+
r[k] = "Resource"
132+
} else {
133+
r[k] = "Unknown"
134+
}
135+
}
136+
return r
137+
}
138+
139+
func (i ActionsInput) Descriptions() map[string]string {
140+
r := map[string]string{}
141+
for k, a := range i.Actions {
142+
r[k] = a.Description
143+
}
144+
return r
145+
}
146+
147+
func (i ActionsInput) Timeouts() map[string]string {
148+
r := map[string]string{}
149+
for k, a := range i.Actions {
150+
if a.Timeout != nil {
151+
r[k] = fmt.Sprintf("%d * time.Second // %s", a.Timeout.Duration/time.Second, a.Timeout.Duration.String())
152+
} else {
153+
r[k] = "ActionsDefaultTimeout"
154+
}
155+
}
156+
return r
157+
}
158+
159+
type Action struct {
160+
Timeout *meta.Duration `json:"timeout,omitempty"`
161+
StartupFailureGracePeriod *meta.Duration `json:"startupFailureGracePeriod,omitempty"`
162+
163+
Scopes []string `json:"scopes,omitempty"`
164+
165+
Description string `json:"description"`
166+
167+
Enterprise bool `json:"enterprise"`
168+
}
169+
170+
func (a Action) InScope(scope string) bool {
171+
if a.Scopes == nil {
172+
return strings.Title(scope) == "Normal"
173+
}
174+
175+
for _, x := range a.Scopes {
176+
if strings.Title(scope) == strings.Title(x) {
177+
return true
178+
}
179+
}
180+
181+
return false
182+
}
183+
184+
func RenderActions(root string) error {
185+
var in ActionsInput
186+
187+
if err := yaml.Unmarshal(actions, &in); err != nil {
188+
return err
189+
}
190+
191+
{
192+
actions := path.Join(root, "pkg", "apis", "deployment", "v1", "actions.generated.go")
193+
194+
out, err := os.OpenFile(actions, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
195+
if err != nil {
196+
return err
197+
}
198+
199+
i, err := template.New("actions").Parse(string(actionsGoTemplate))
200+
if err != nil {
201+
return err
202+
}
203+
204+
if err := i.Execute(out, map[string]interface{}{
205+
"actions": in.Keys(),
206+
"scopes": in.Scopes(),
207+
"highestScopes": in.HighestScopes(),
208+
"timeouts": in.Timeouts(),
209+
"descriptions": in.Descriptions(),
210+
"defaultTimeout": fmt.Sprintf("%d * time.Second // %s", in.DefaultTimeout.Duration/time.Second, in.DefaultTimeout.Duration.String()),
211+
}); err != nil {
212+
return err
213+
}
214+
215+
if err := out.Close(); err != nil {
216+
return err
217+
}
218+
}
219+
220+
{
221+
actions := path.Join(root, "pkg", "deployment", "reconcile", "action.register.generated.go")
222+
223+
out, err := os.OpenFile(actions, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
224+
if err != nil {
225+
return err
226+
}
227+
228+
i, err := template.New("actions").Parse(string(actionsRegisterGoTemplate))
229+
if err != nil {
230+
return err
231+
}
232+
233+
if err := i.Execute(out, map[string]interface{}{
234+
"actions": in.Keys(),
235+
"startupFailureGracePeriods": in.StartFailureGracePeriods(),
236+
}); err != nil {
237+
return err
238+
}
239+
240+
if err := out.Close(); err != nil {
241+
return err
242+
}
243+
}
244+
245+
{
246+
actions := path.Join(root, "pkg", "deployment", "reconcile", "action.register.generated_test.go")
247+
248+
out, err := os.OpenFile(actions, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
249+
if err != nil {
250+
return err
251+
}
252+
253+
i, err := template.New("actions").Parse(string(actionsRegisterTestGoTemplate))
254+
if err != nil {
255+
return err
256+
}
257+
258+
if err := i.Execute(out, map[string]interface{}{
259+
"actions": in.Keys(),
260+
"startupFailureGracePeriods": in.StartFailureGracePeriods(),
261+
}); err != nil {
262+
return err
263+
}
264+
265+
if err := out.Close(); err != nil {
266+
return err
267+
}
268+
}
269+
270+
{
271+
actions := path.Join(root, "docs", "generated", "actions.md")
272+
273+
out, err := os.OpenFile(actions, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
274+
if err != nil {
275+
return err
276+
}
277+
278+
i, err := template.New("actions").Parse(string(actionsMD))
279+
if err != nil {
280+
return err
281+
}
282+
283+
action := md.NewColumn("Action", md.ColumnCenterAlign)
284+
timeout := md.NewColumn("Timeout", md.ColumnCenterAlign)
285+
description := md.NewColumn("Description", md.ColumnCenterAlign)
286+
edition := md.NewColumn("Edition", md.ColumnCenterAlign)
287+
t := md.NewTable(
288+
action,
289+
timeout,
290+
edition,
291+
description,
292+
)
293+
294+
for _, k := range in.Keys() {
295+
a := in.Actions[k]
296+
v := in.DefaultTimeout.Duration.String()
297+
if t := a.Timeout; t != nil {
298+
v = t.Duration.String()
299+
}
300+
301+
vr := "Community & Enterprise"
302+
if a.Enterprise {
303+
vr = "Enterprise Only"
304+
}
305+
306+
if err := t.AddRow(map[md.Column]string{
307+
action: k,
308+
timeout: v,
309+
description: a.Description,
310+
edition: vr,
311+
}); err != nil {
312+
return err
313+
}
314+
}
315+
316+
timeouts := api.ActionTimeouts{}
317+
318+
for _, k := range in.Keys() {
319+
a := in.Actions[k]
320+
if a.Timeout != nil {
321+
timeouts[api.ActionType(k)] = api.NewTimeout(a.Timeout.Duration)
322+
} else {
323+
timeouts[api.ActionType(k)] = api.NewTimeout(in.DefaultTimeout.Duration)
324+
}
325+
}
326+
327+
d, err := yaml.Marshal(map[string]interface{}{
328+
"spec": map[string]interface{}{
329+
"timeouts": map[string]interface{}{
330+
"actions": timeouts,
331+
},
332+
},
333+
})
334+
if err != nil {
335+
return err
336+
}
337+
338+
if err := i.Execute(out, map[string]interface{}{
339+
"table": t.Render(),
340+
"example": string(d),
341+
}); err != nil {
342+
return err
343+
}
344+
345+
if err := out.Close(); err != nil {
346+
return err
347+
}
348+
}
349+
350+
return nil
351+
}

0 commit comments

Comments
 (0)