-
Notifications
You must be signed in to change notification settings - Fork 267
/
crd.go
155 lines (129 loc) · 6.08 KB
/
crd.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
150
151
152
153
154
155
// Copyright 2018 Comcast Cable Communications Management, LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"errors"
"strings"
"time"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/Comcast/kuberhealthy/v2/pkg/apis/khjob/v1"
"github.com/Comcast/kuberhealthy/v2/pkg/health"
"github.com/Comcast/kuberhealthy/v2/pkg/khstatecrd"
)
// setCheckStateResource puts a check state's state into the specified CRD resource. It sets the AuthoritativePod
// to the server's hostname and sets the LastUpdate time to now.
func setCheckStateResource(checkName string, checkNamespace string, state health.WorkloadDetails) error {
name := sanitizeResourceName(checkName)
// we must fetch the existing state to use the current resource version
// int found within
existingState, err := khStateClient.Get(metav1.GetOptions{}, stateCRDResource, name, checkNamespace)
if err != nil {
return errors.New("Error retrieving CRD for: " + name + " " + err.Error())
}
resourceVersion := existingState.GetResourceVersion()
// set the pod name that wrote the khstate
state.AuthoritativePod = podHostname
state.LastRun = time.Now() // set the time the khstate was last
khState := khstatecrd.NewKuberhealthyState(name, state)
khState.SetResourceVersion(resourceVersion)
// TODO - if "try again" message found in error, then try again
log.Debugln(checkNamespace, checkName, "writing khstate with ok:", state.OK, "and errors:", state.Errors, "at last run:", state.LastRun)
_, err = khStateClient.Update(&khState, stateCRDResource, name, checkNamespace)
return err
}
// sanitizeResourceName cleans up the check names for use in CRDs.
// DNS-1123 subdomains must consist of lower case alphanumeric characters, '-'
// or '.', and must start and end with an alphanumeric character (e.g.
// 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?
// (\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
func sanitizeResourceName(c string) string {
// the name we pass to the CRD must be lowercase
nameLower := strings.ToLower(c)
return strings.Replace(nameLower, " ", "-", -1)
}
// ensureStateResourceExists checks for the existence of the specified resource and creates it if it does not exist
func ensureStateResourceExists(checkName string, checkNamespace string, workload health.KHWorkload) error {
name := sanitizeResourceName(checkName)
log.Debugln("Checking existence of custom resource:", name)
state, err := khStateClient.Get(metav1.GetOptions{}, stateCRDResource, name, checkNamespace)
if err != nil {
if k8sErrors.IsNotFound(err) || strings.Contains(err.Error(), "not found") {
log.Infoln("Custom resource not found, creating resource:", name, " - ", err)
initialDetails := health.NewWorkloadDetails(workload)
initialState := khstatecrd.NewKuberhealthyState(name, initialDetails)
_, err := khStateClient.Create(&initialState, stateCRDResource, checkNamespace)
if err != nil {
return errors.New("Error creating custom resource: " + name + ": " + err.Error())
}
} else {
return err
}
}
if state.Spec.Errors != nil {
log.Debugln("khstate custom resource found:", name)
}
return nil
}
// getCheckState retrieves the check values from the kuberhealthy khstate
// custom resource
func getCheckState(c KuberhealthyCheck) (health.WorkloadDetails, error) {
var state = health.NewWorkloadDetails(health.KHCheck)
var err error
name := sanitizeResourceName(c.Name())
// make sure the CRD exists, even when checking status
err = ensureStateResourceExists(c.Name(), c.CheckNamespace(), health.KHCheck)
if err != nil {
return state, errors.New("Error validating CRD exists: " + name + " " + err.Error())
}
log.Debugln("Retrieving khstate custom resource for:", name)
khstate, err := khStateClient.Get(metav1.GetOptions{}, stateCRDResource, name, c.CheckNamespace())
if err != nil {
return state, errors.New("Error retrieving custom khstate resource: " + name + " " + err.Error())
}
log.Debugln("Successfully retrieved khstate resource:", name)
return khstate.Spec, nil
}
// getCheckState retrieves the check values from the kuberhealthy khstate
// custom resource
func getJobState(j KuberhealthyCheck) (health.WorkloadDetails, error) {
var state = health.NewWorkloadDetails(health.KHJob)
var err error
name := sanitizeResourceName(j.Name())
// make sure the CRD exists, even when checking status
err = ensureStateResourceExists(j.Name(), j.CheckNamespace(), health.KHJob)
if err != nil {
return state, errors.New("Error validating CRD exists: " + name + " " + err.Error())
}
log.Debugln("Retrieving khstate custom resource for:", name)
khstate, err := khStateClient.Get(metav1.GetOptions{}, stateCRDResource, name, j.CheckNamespace())
if err != nil {
return state, errors.New("Error retrieving custom khstate resource: " + name + " " + err.Error())
}
log.Debugln("Successfully retrieved khstate resource:", name)
return khstate.Spec, nil
}
// setJobPhase updates the kuberhealthy job phase depending on the state of its run.
func setJobPhase(jobName string, jobNamespace string, jobPhase v1.JobPhase) error {
kj, err := khJobClient.KuberhealthyJobs(jobNamespace).Get(jobName, metav1.GetOptions{})
if err != nil {
log.Errorln("error getting khjob:", jobName, err)
return err
}
resourceVersion := kj.GetResourceVersion()
updatedJob := v1.NewKuberhealthyJob(jobName, jobNamespace, kj.Spec)
updatedJob.SetResourceVersion(resourceVersion)
log.Infoln("Setting khjob phase to:", jobPhase)
updatedJob.Spec.Phase = jobPhase
_, err = khJobClient.KuberhealthyJobs(jobNamespace).Update(&updatedJob)
return err
}