1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package main
17
18 import (
19 "bytes"
20 "text/template"
21 )
22
23 const implImport = `
24 import (
25 "encoding/json"
26 "reflect"
27 "time"
28
29 "github.com/pkg/errors"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/runtime"
32 logf "sigs.k8s.io/controller-runtime/pkg/log"
33 "sigs.k8s.io/controller-runtime/pkg/webhook"
34 "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
35
36 gw "github.com/chaos-mesh/chaos-mesh/api/genericwebhook"
37 )
38
39 // updating spec of a chaos will have no effect, we'd better reject it
40 var ErrCanNotUpdateChaos = errors.New("Cannot update chaos spec")
41 `
42
43 const implTemplate = `
44 const Kind{{.Type}} = "{{.Type}}"
45 {{if .IsExperiment}}
46 // IsDeleted returns whether this resource has been deleted
47 func (in *{{.Type}}) IsDeleted() bool {
48 return !in.DeletionTimestamp.IsZero()
49 }
50
51 // IsPaused returns whether this resource has been paused
52 func (in *{{.Type}}) IsPaused() bool {
53 if in.Annotations == nil || in.Annotations[PauseAnnotationKey] != "true" {
54 return false
55 }
56 return true
57 }
58
59 // GetObjectMeta would return the ObjectMeta for chaos
60 func (in *{{.Type}}) GetObjectMeta() *metav1.ObjectMeta {
61 return &in.ObjectMeta
62 }
63
64 // GetDuration would return the duration for chaos
65 func (in *{{.Type}}Spec) GetDuration() (*time.Duration, error) {
66 if in.Duration == nil {
67 return nil, nil
68 }
69 duration, err := time.ParseDuration(string(*in.Duration))
70 if err != nil {
71 return nil, err
72 }
73 return &duration, nil
74 }
75
76 // GetStatus returns the status
77 func (in *{{.Type}}) GetStatus() *ChaosStatus {
78 return &in.Status.ChaosStatus
79 }
80
81 // GetRemoteCluster returns the remoteCluster
82 func (in *{{.Type}}) GetRemoteCluster() string {
83 return in.Spec.RemoteCluster
84 }
85
86 // GetSpecAndMetaString returns a string including the meta and spec field of this chaos object.
87 func (in *{{.Type}}) GetSpecAndMetaString() (string, error) {
88 spec, err := json.Marshal(in.Spec)
89 if err != nil {
90 return "", err
91 }
92
93 meta := in.ObjectMeta.DeepCopy()
94 meta.SetResourceVersion("")
95 meta.SetGeneration(0)
96
97 return string(spec) + meta.String(), nil
98 }
99
100 // +kubebuilder:object:root=true
101
102 // {{.Type}}List contains a list of {{.Type}}
103 type {{.Type}}List struct {
104 metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + `
105 metav1.ListMeta ` + "`" + `json:"metadata,omitempty"` + "`" + `
106 Items []{{.Type}} ` + "`" + `json:"items"` + "`" + `
107 }
108
109 func (in *{{.Type}}List) DeepCopyList() GenericChaosList {
110 return in.DeepCopy()
111 }
112
113 // ListChaos returns a list of chaos
114 func (in *{{.Type}}List) ListChaos() []GenericChaos {
115 var result []GenericChaos
116 for _, item := range in.Items {
117 item := item
118 result = append(result, &item)
119 }
120 return result
121 }
122
123 func (in *{{.Type}}) DurationExceeded(now time.Time) (bool, time.Duration, error) {
124 duration, err := in.Spec.GetDuration()
125 if err != nil {
126 return false, 0, err
127 }
128
129 if duration != nil {
130 stopTime := in.GetCreationTimestamp().Add(*duration)
131 if stopTime.Before(now) {
132 return true, 0, nil
133 }
134
135 return false, stopTime.Sub(now), nil
136 }
137
138 return false, 0, nil
139 }
140
141 func (in *{{.Type}}) IsOneShot() bool {
142 {{- if .OneShotExp}}
143 if {{.OneShotExp}} {
144 return true
145 }
146
147 return false
148 {{- else}}
149 return false
150 {{- end}}
151 }
152 {{end}}
153 var {{.Type}}WebhookLog = logf.Log.WithName("{{.Type}}-resource")
154
155 func (in *{{.Type}}) ValidateCreate() (admission.Warnings, error) {
156 {{.Type}}WebhookLog.Info("validate create", "name", in.Name)
157 return in.Validate()
158 }
159
160 // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
161 func (in *{{.Type}}) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
162 {{.Type}}WebhookLog.Info("validate update", "name", in.Name)
163 {{- if not .EnableUpdate}}
164 if !reflect.DeepEqual(in.Spec, old.(*{{.Type}}).Spec) {
165 return nil, ErrCanNotUpdateChaos
166 }
167 {{- end}}
168 return in.Validate()
169 }
170
171 // ValidateDelete implements webhook.Validator so a webhook will be registered for the type
172 func (in *{{.Type}}) ValidateDelete() (admission.Warnings, error) {
173 {{.Type}}WebhookLog.Info("validate delete", "name", in.Name)
174
175 // Nothing to do?
176 return nil, nil
177 }
178
179 var _ webhook.Validator = &{{.Type}}{}
180
181 func (in *{{.Type}}) Validate() ([]string, error) {
182 errs := gw.Validate(in)
183 return nil, gw.Aggregate(errs)
184 }
185
186 var _ webhook.Defaulter = &{{.Type}}{}
187
188 func (in *{{.Type}}) Default() {
189 gw.Default(in)
190 }
191 `
192
193 func generateImpl(name string, oneShotExp string, isExperiment, enableUpdate bool) string {
194 tmpl, err := template.New("impl").Parse(implTemplate)
195 if err != nil {
196 log.Error(err, "fail to build template")
197 return ""
198 }
199
200 buf := new(bytes.Buffer)
201 err = tmpl.Execute(buf, &metadata{
202 Type: name,
203 OneShotExp: oneShotExp,
204 IsExperiment: isExperiment,
205 EnableUpdate: enableUpdate,
206 })
207 if err != nil {
208 log.Error(err, "fail to execute template")
209 return ""
210 }
211
212 return buf.String()
213 }
214