Skip to content

Commit 7d2d038

Browse files
author
Amit Kumar Das
authored
feat(http): add http as a custom resource (#107)
This commit adds HTTP as a custom resource. This can currently be used to invoke http requests that do not need any authentication. Few e2e experiments added to verify HTTP custom resource. This change also includes a minor bug fix w.r.t StateCheck assert action. Before this fix any timeout error resulted in Recipe's status.phase being set as 'Error'. With current changes any timeout error will result in Recipe's status.phase as 'Failed'. In addition, ci related artifacts including experiments & yaml comments have been updated to make the ci run faster as well as make it more readable for contributors. Signed-off-by: AmitKumarDas <[email protected]>
1 parent b1ef09d commit 7d2d038

25 files changed

+455
-102
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ d-operators
33
test/bin/
44
test/kubebin/
55
test/e2e/uninstall-k3s.txt
6+
uninstall-k3s.txt
7+
dope

Makefile

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
PWD := ${CURDIR}
2-
3-
OS = $(shell uname)
4-
5-
PACKAGE_VERSION ?= $(shell git describe --always --tags)
6-
GIT_TAGS = $(shell git fetch --all --tags)
1+
# Fetch the latest tags & then set the package version
2+
PACKAGE_VERSION ?= $(shell git fetch --all --tags | echo "" | git describe --always --tags)
73
ALL_SRC = $(shell find . -name "*.go" | grep -v -e "vendor")
84

95
# We are using docker hub as the default registry
10-
#IMG_REGISTRY ?= quay.io
116
IMG_NAME ?= dope
127
IMG_REPO ?= mayadataio/dope
138

@@ -22,8 +17,6 @@ $(IMG_NAME): $(ALL_SRC)
2217

2318
$(ALL_SRC): ;
2419

25-
$(GIT_TAGS): ;
26-
2720
# go mod download modules to local cache
2821
# make vendored copy of dependencies
2922
# install other go binaries for code generation
@@ -46,9 +39,9 @@ e2e-test:
4639
@cd test/e2e && ./suite.sh
4740

4841
.PHONY: image
49-
image: $(GIT_TAGS)
42+
image:
5043
docker build -t $(IMG_REPO):$(PACKAGE_VERSION) .
5144

5245
.PHONY: push
5346
push: image
54-
docker push $(IMG_REPO):$(PACKAGE_VERSION
47+
docker push $(IMG_REPO):$(PACKAGE_VERSION)

config/metac.yaml

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1-
apiVersion: metac.openebs.io/v1alpha1
1+
apiVersion: dope/v1
22
kind: GenericController
33
metadata:
44
name: sync-recipe
55
namespace: dope
66
spec:
7-
watch: # kind: Recipe custom resource is watched
7+
watch:
8+
# kind: Recipe custom resource is watched
89
apiVersion: dope.metacontroller.io/v1
910
resource: recipes
1011
hooks:
1112
sync:
1213
inline:
1314
funcName: sync/recipe
15+
---
16+
apiVersion: dope/v1
17+
kind: GenericController
18+
metadata:
19+
name: sync-http
20+
namespace: dope
21+
spec:
22+
watch:
23+
# kind: HTTP custom resource is watched
24+
apiVersion: dope.metacontroller.io/v1
25+
resource: https
26+
hooks:
27+
sync:
28+
inline:
29+
funcName: sync/http
1430
---

controller/http/reconciler.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,24 @@ func (r *Reconciler) evalObservedHTTP() {
7474
}
7575
r.observedHTTP = &http
7676

77-
// validate presence of secret
77+
// extract secret name if set
7878
r.observedSecretName = http.Spec.SecretName
79-
if r.observedSecretName == "" {
80-
r.Err = errors.Errorf("Missing spec.secretName")
81-
return
82-
}
79+
80+
// validate presence of secret
81+
// if r.observedSecretName == "" {
82+
// r.Err = errors.Errorf("Missing spec.secretName")
83+
// return
84+
// }
8385
}
8486

8587
// evalObservedSecret parses the relevant secret found in Kubernetes
8688
// cluster. This secret is used to authenticate the request invocation.
8789
func (r *Reconciler) evalObservedSecret() {
90+
if r.observedSecretName == "" {
91+
// absence of secret name implies http invocation does not
92+
// require authentication
93+
return
94+
}
8895
r.observedSecret = r.HookRequest.Attachments.FindByGroupKindName(
8996
"v1",
9097
"Secret",
@@ -199,7 +206,7 @@ func (r *Reconciler) handleRuntimeError() {
199206
// NOTE:
200207
// Status forms the core business logic of reconciling a HTTP
201208
// custom resource.
202-
func (r *Reconciler) updateWatchStatus() {
209+
func (r *Reconciler) updateHTTPStatus() {
203210
// check for runtime errors
204211
if r.Err != nil {
205212
r.handleRuntimeError()
@@ -277,7 +284,7 @@ func Sync(request *generic.SyncHookRequest, response *generic.SyncHookResponse)
277284
// NOTE:
278285
// HTTP custom resource is the watch here
279286
r.DesiredWatchFns = []func(){
280-
r.updateWatchStatus,
287+
r.updateHTTPStatus,
281288
}
282289

283290
// run reconcile

controller/recipe/reconciler.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (r *Reconciler) setSyncResponse() {
7474
}
7575
}
7676

77-
func (r *Reconciler) setWatchStatusAsError() {
77+
func (r *Reconciler) setRecipeStatusAsError() {
7878
r.HookResponse.Status = map[string]interface{}{
7979
"phase": "Error",
8080
"reason": r.Err.Error(),
@@ -84,7 +84,7 @@ func (r *Reconciler) setWatchStatusAsError() {
8484
}
8585
}
8686

87-
func (r *Reconciler) setWatchStatusFromRecipeStatus() {
87+
func (r *Reconciler) setRecipeStatus() {
8888
r.HookResponse.Status = map[string]interface{}{
8989
"phase": string(r.RecipeStatus.Phase),
9090
"reason": r.RecipeStatus.Reason,
@@ -110,15 +110,15 @@ func (r *Reconciler) setWatchStatus() {
110110
r.HookResponse.ResyncAfterSeconds =
111111
*r.ObservedRecipe.Spec.Refresh.OnErrorResyncAfterSeconds
112112
}
113-
r.setWatchStatusAsError()
113+
r.setRecipeStatusAsError()
114114
return
115115
}
116116
if r.RecipeStatus.Phase == types.RecipeStatusLocked {
117117
// nothing needs to be done
118118
// old status will persist
119119
return
120120
}
121-
r.setWatchStatusFromRecipeStatus()
121+
r.setRecipeStatus()
122122
}
123123

124124
// Sync implements the idempotent logic to sync Recipe resource

manifests/crd.yaml

+23
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ spec:
1818
status: {}
1919
version: v1
2020
versions:
21+
- name: v1
22+
served: true
23+
storage: true
24+
---
25+
apiVersion: apiextensions.k8s.io/v1beta1
26+
kind: CustomResourceDefinition
27+
metadata:
28+
annotations:
29+
name: https.dope.metacontroller.io
30+
spec:
31+
group: dope.metacontroller.io
32+
names:
33+
kind: HTTP
34+
listKind: HTTPList
35+
plural: https
36+
shortNames:
37+
- http
38+
singular: http
39+
scope: Namespaced
40+
subresources:
41+
status: {}
42+
version: v1
43+
versions:
2144
- name: v1
2245
served: true
2346
storage: true

pkg/http/http.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,20 @@ func (i *Invocable) buildStatus(response *resty.Response) types.HTTPResponse {
8080
// Invoke executes the http request
8181
func (i *Invocable) Invoke() (types.HTTPResponse, error) {
8282
req := resty.New().R().
83-
SetBasicAuth(i.Username, i.Password).
8483
SetBody(i.Body).
8584
SetHeaders(i.Headers).
8685
SetQueryParams(i.QueryParams).
8786
SetPathParams(i.PathParams)
8887

88+
// set credentials only if it was provided
89+
if i.Username != "" || i.Password != "" {
90+
req.SetBasicAuth(i.Username, i.Password)
91+
}
92+
8993
var response *resty.Response
9094
var err error
9195

92-
switch strings.ToLower(i.HTTPMethod) {
96+
switch strings.ToUpper(i.HTTPMethod) {
9397
case types.POST:
9498
response, err = req.Post(i.URL)
9599
case types.GET:

pkg/recipe/recipe.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func (r *Runner) buildLockRunner() *LockRunner {
212212
}
213213

214214
// evalAll evaluates all tasks
215-
func (r *Runner) evalAll() error {
215+
func (r *Runner) evalAllTasks() error {
216216
for _, task := range r.Recipe.Spec.Tasks {
217217
err := r.eval(task)
218218
if err != nil {
@@ -257,7 +257,7 @@ func (r *Runner) addRecipeElapsedTimeInSeconds(elapsedtime float64) {
257257
}
258258

259259
// runAll runs all the tasks
260-
func (r *Runner) runAll() (status *types.RecipeStatus, err error) {
260+
func (r *Runner) runAllTasks() (status *types.RecipeStatus, err error) {
261261
defer func() {
262262
r.fixture.TearDown()
263263
}()
@@ -280,6 +280,9 @@ func (r *Runner) runAll() (status *types.RecipeStatus, err error) {
280280
}
281281
got, err := tr.Run()
282282
if err != nil {
283+
// We discontinue executing next tasks
284+
// if current task execution resulted in
285+
// error
283286
return nil, errors.Wrapf(
284287
err,
285288
"Failed to run task [%d] %q",
@@ -289,6 +292,8 @@ func (r *Runner) runAll() (status *types.RecipeStatus, err error) {
289292
}
290293
r.RecipeStatus.TaskListStatus[task.Name] = got
291294
if got.Phase == types.TaskStatusFailed {
295+
// We run subsequent tasks even if current task
296+
// failed
292297
failedTasks++
293298
}
294299
}
@@ -360,7 +365,7 @@ func (r *Runner) Run() (status *types.RecipeStatus, err error) {
360365
}()
361366
r.RecipeStatus.TaskListStatus[r.Recipe.GetName()+"-lock"] = lockstatus
362367

363-
err = r.evalAll()
368+
err = r.evalAllTasks()
364369
if err != nil {
365370
return nil, err
366371
}
@@ -380,5 +385,5 @@ func (r *Runner) Run() (status *types.RecipeStatus, err error) {
380385
}, nil
381386
}
382387

383-
return r.runAll()
388+
return r.runAllTasks()
384389
}

pkg/recipe/recipe_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ func TestRunnerRunAllTasks(t *testing.T) {
322322
},
323323
fixture: f,
324324
}
325-
r.initEnabled() // init to avoid nil pointers
326-
got, err := r.runAll() // method under test
325+
r.initEnabled() // init to avoid nil pointers
326+
got, err := r.runAllTasks() // method under test
327327
if mock.isErr && err == nil {
328328
t.Fatal("Expected error got none")
329329
}

pkg/recipe/state_check.go

+31-10
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,13 @@ func (sc *StateChecking) assertEquals() {
184184
sc.retryOnDiff = true
185185
success, err := sc.isMergeEqualsObserved(message)
186186
if err != nil {
187-
sc.err = err
188-
return
187+
// verify if this was a timeout error
188+
if _, ok := err.(*RetryTimeout); !ok {
189+
sc.err = err
190+
return
191+
}
192+
// set the timeout error against corresponding status field
193+
sc.result.Timeout = err.Error()
189194
}
190195
// init phase as failed
191196
sc.result.Phase = types.StateCheckResultFailed
@@ -209,8 +214,14 @@ func (sc *StateChecking) assertNotEquals() {
209214
sc.retryOnEqual = true
210215
success, err := sc.isMergeEqualsObserved(message)
211216
if err != nil {
212-
sc.err = err
213-
return
217+
// verify if this is a timeout error
218+
if _, ok := err.(*RetryTimeout); !ok {
219+
// this is a runtime error
220+
sc.err = err
221+
return
222+
}
223+
// set timeout error against corresponding status field
224+
sc.result.Timeout = err.Error()
214225
}
215226
// init phase as failed
216227
sc.result.Phase = types.StateCheckResultFailed
@@ -251,21 +262,22 @@ func (sc *StateChecking) assertNotFound() {
251262
if apierrors.IsNotFound(err) {
252263
// phase is set to Passed here
253264
phase = types.StateCheckResultPassed
254-
// Stop retrying
265+
// Stop retrying since resource is not found in the cluster
255266
return true, nil
256267
}
257-
// Keep retrying
268+
// Keep retrying since get call errored out
258269
return false, err
259270
}
260271
if len(got.GetFinalizers()) == 0 && got.GetDeletionTimestamp() != nil {
261272
phase = types.StateCheckResultWarning
262273
warning = fmt.Sprintf(
263-
"Marking StateCheck %q to passed: Finalizer count %d: Deletion timestamp %s",
274+
"Marking StateCheck %q to passed: Finalizer count %d: Deletion timestamp %q",
264275
sc.TaskName,
265276
len(got.GetFinalizers()),
266277
got.GetDeletionTimestamp(),
267278
)
268-
// Stop retrying
279+
// Stop retrying since Kubernetes has marked the resource
280+
// to be deleted
269281
return true, nil
270282
}
271283
// Keep retrying
@@ -274,10 +286,13 @@ func (sc *StateChecking) assertNotFound() {
274286
message,
275287
)
276288
if err != nil {
289+
// verify if this is a timeout error
277290
if _, ok := err.(*RetryTimeout); !ok {
291+
// this is a runtime error
278292
sc.err = err
279293
return
280294
}
295+
// set timeout error against corresponding status field
281296
sc.result.Timeout = err.Error()
282297
}
283298
sc.result.Phase = phase
@@ -335,15 +350,18 @@ func (sc *StateChecking) assertListCountEquals() {
335350
message,
336351
)
337352
if err != nil {
353+
// verify if this is a timeout error
338354
if _, ok := err.(*RetryTimeout); !ok {
355+
// this is a runtime error
339356
sc.err = err
340357
return
341358
}
359+
// set timeout error against corresponding status field
342360
sc.result.Timeout = err.Error()
343361
}
344362
sc.result.Phase = phase
345363
sc.result.Message = message
346-
sc.result.Warning = fmt.Sprintf(
364+
sc.result.Verbose = fmt.Sprintf(
347365
"Expected count %d got %d",
348366
*sc.StateCheck.Count,
349367
sc.actualListCount,
@@ -377,15 +395,18 @@ func (sc *StateChecking) assertListCountNotEquals() {
377395
message,
378396
)
379397
if err != nil {
398+
// verify if this this a timeout error
380399
if _, ok := err.(*RetryTimeout); !ok {
400+
// this is a runtime error
381401
sc.err = err
382402
return
383403
}
404+
// set timeout error against corresponding status field
384405
sc.result.Timeout = err.Error()
385406
}
386407
sc.result.Phase = phase
387408
sc.result.Message = message
388-
sc.result.Warning = fmt.Sprintf(
409+
sc.result.Verbose = fmt.Sprintf(
389410
"Expected count %d got %d",
390411
*sc.StateCheck.Count,
391412
sc.actualListCount,

0 commit comments

Comments
 (0)