• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sapcc / maintenance-controller / 6193045518

15 Sep 2023 02:07AM UTC coverage: 73.916% (+0.4%) from 73.552%
6193045518

push

github

renovate[bot]
Update module go.uber.org/zap to v1.26.0

2437 of 3297 relevant lines covered (73.92%)

0.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/controllers/node_handler.go
1
/*******************************************************************************
2
*
3
* Copyright 2020 SAP SE
4
*
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You should have received a copy of the License along with this
8
* program. If not, 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
*******************************************************************************/
19

20
package controllers
21

22
import (
23
        "errors"
24
        "fmt"
25
        "time"
26

27
        "github.com/sapcc/maintenance-controller/constants"
28
        "github.com/sapcc/maintenance-controller/metrics"
29
        "github.com/sapcc/maintenance-controller/plugin"
30
        "github.com/sapcc/maintenance-controller/state"
31
)
32

33
type NodeHandler = func(params reconcileParameters, data *state.DataV2) error
34

35
var handlers []NodeHandler = []NodeHandler{
36
        EnsureLabelMap,
37
        MaintainProfileStates,
38
        ApplyProfiles,
39
        UpdateMaintenanceStateLabel,
40
}
41

42
func HandleNode(params reconcileParameters, data *state.DataV2) error {
1✔
43
        for _, handler := range handlers {
2✔
44
                if err := handler(params, data); err != nil {
1✔
45
                        return err
×
46
                }
×
47
        }
48
        return nil
1✔
49
}
50

51
func EnsureLabelMap(params reconcileParameters, data *state.DataV2) error {
1✔
52
        if params.node.Labels == nil {
2✔
53
                params.node.Labels = make(map[string]string)
1✔
54
        }
1✔
55
        return nil
1✔
56
}
57

58
// ensure a profile is assigned beforehand.
59
func MaintainProfileStates(params reconcileParameters, data *state.DataV2) error {
1✔
60
        profilesStr := params.node.Labels[constants.ProfileLabelKey]
1✔
61
        data.MaintainProfileStates(profilesStr, params.config.Profiles)
1✔
62
        return nil
1✔
63
}
1✔
64

65
// ensure a profile is assigned and profile states have been maintained beforehand.
66
func ApplyProfiles(params reconcileParameters, data *state.DataV2) error {
1✔
67
        profilesStr := params.node.Labels[constants.ProfileLabelKey]
1✔
68
        profileStates := data.GetProfilesWithState(profilesStr, params.config.Profiles)
1✔
69
        profileResults, errs := make([]state.ProfileResult, 0), make([]error, 0)
1✔
70
        for _, ps := range profileStates {
2✔
71
                err := metrics.TouchShuffles(params.ctx, params.client, params.node, ps.Profile.Name)
1✔
72
                if err != nil {
1✔
73
                        params.log.Info("failed to touch shuffle metrics", "profile", ps.Profile.Name, "error", err)
×
74
                }
×
75
                // construct state
76
                stateObj, err := state.FromLabel(ps.State, ps.Profile.Chains[ps.State])
1✔
77
                if err != nil {
1✔
78
                        errs = append(errs, fmt.Errorf("failed to create internal state from unknown label value: %w", err))
×
79
                        continue
×
80
                }
81

82
                logDetails := false
1✔
83
                if params.node.Labels[constants.LogDetailsLabelKey] == "true" {
2✔
84
                        logDetails = true
1✔
85
                }
1✔
86
                // build plugin arguments
87
                pluginParams := plugin.Parameters{Client: params.client, Ctx: params.ctx, Log: params.log,
1✔
88
                        Profile: ps.Profile.Name, Node: params.node, InMaintenance: anyInMaintenance(profileStates),
1✔
89
                        State: string(ps.State), LastTransition: data.Profiles[ps.Profile.Name].Transition,
1✔
90
                        Recorder: params.recorder, LogDetails: logDetails}
1✔
91

1✔
92
                applied, err := state.Apply(stateObj, params.node, data, pluginParams)
1✔
93
                profileResults = append(profileResults, state.ProfileResult{
1✔
94
                        Applied: applied,
1✔
95
                        Name:    ps.Profile.Name,
1✔
96
                        State:   stateObj.Label(),
1✔
97
                })
1✔
98
                if err != nil {
1✔
99
                        errs = append(errs, err)
×
100
                }
×
101
        }
102
        params.nodeInfoCache.Update(state.NodeInfo{
1✔
103
                Node:     params.node.Name,
1✔
104
                Profiles: profileResults,
1✔
105
                Labels:   filterNodeLabels(params.node.Labels, params.config.DashboardLabelFilter),
1✔
106
        })
1✔
107
        if len(errs) > 0 {
1✔
108
                return fmt.Errorf("failed to apply current state: %w", errors.Join(errs...))
×
109
        }
×
110
        for i, ps := range profileStates {
2✔
111
                result := profileResults[i]
1✔
112
                // check if a transition happened
1✔
113
                if ps.State != result.Applied.Next {
2✔
114
                        data.Profiles[ps.Profile.Name].Transition = time.Now().UTC()
1✔
115
                        data.Profiles[ps.Profile.Name].Current = result.Applied.Next
1✔
116
                }
1✔
117
                // track the state of this reconciliation for the next run
118
                data.Profiles[ps.Profile.Name].Previous = result.State
1✔
119
        }
120
        return nil
1✔
121
}
122

123
func anyInMaintenance(profileStates []state.ProfileState) bool {
1✔
124
        for _, ps := range profileStates {
2✔
125
                if ps.State == state.InMaintenance {
2✔
126
                        return true
1✔
127
                }
1✔
128
        }
129
        return false
1✔
130
}
131

132
func filterNodeLabels(nodeLabels map[string]string, keys []string) map[string]string {
1✔
133
        result := make(map[string]string)
1✔
134
        for _, key := range keys {
2✔
135
                val, ok := nodeLabels[key]
1✔
136
                if ok {
2✔
137
                        result[key] = val
1✔
138
                }
1✔
139
        }
140
        return result
1✔
141
}
142

143
func UpdateMaintenanceStateLabel(params reconcileParameters, data *state.DataV2) error {
1✔
144
        profilesStr := params.node.Labels[constants.ProfileLabelKey]
1✔
145
        profileStates := data.GetProfilesWithState(profilesStr, params.config.Profiles)
1✔
146
        if params.node.Labels == nil {
1✔
147
                params.node.Labels = make(map[string]string)
×
148
        }
×
149
        for _, ps := range profileStates {
2✔
150
                if ps.State == state.InMaintenance {
2✔
151
                        params.node.Labels[constants.StateLabelKey] = string(ps.State)
1✔
152
                        return nil
1✔
153
                }
1✔
154
        }
155
        for _, ps := range profileStates {
2✔
156
                if ps.State == state.Required {
2✔
157
                        params.node.Labels[constants.StateLabelKey] = string(ps.State)
1✔
158
                        return nil
1✔
159
                }
1✔
160
        }
161
        params.node.Labels[constants.StateLabelKey] = string(state.Operational)
1✔
162
        return nil
1✔
163
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc