/
observe.go
108 lines (88 loc) · 3.91 KB
/
observe.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
package request
import (
"context"
"net/http"
"strings"
"github.com/crossplane-contrib/provider-http/apis/request/v1alpha1"
httpClient "github.com/crossplane-contrib/provider-http/internal/clients/http"
"github.com/crossplane-contrib/provider-http/internal/controller/request/requestgen"
"github.com/crossplane-contrib/provider-http/internal/json"
"github.com/crossplane-contrib/provider-http/internal/utils"
"github.com/pkg/errors"
)
const (
errObjectNotFound = "object wasn't found"
errNotValidJSON = "%s is not a valid JSON string: %s"
)
type ObserveRequestDetails struct {
Details httpClient.HttpDetails
ResponseError error
Synced bool
}
// NewObserveRequestDetails is a constructor function that initializes
// an instance of ObserveRequestDetails with default values.
func NewObserve(details httpClient.HttpDetails, resErr error, synced bool) ObserveRequestDetails {
return ObserveRequestDetails{
Synced: synced,
Details: details,
ResponseError: resErr,
}
}
// NewObserveRequestDetails is a constructor function that initializes
// an instance of ObserveRequestDetails with default values.
func FailedObserve() ObserveRequestDetails {
return ObserveRequestDetails{
Synced: false,
}
}
// isUpToDate checks whether desired spec up to date with the observed state for a given request
func (c *external) isUpToDate(ctx context.Context, cr *v1alpha1.Request) (ObserveRequestDetails, error) {
if !c.isObjectValidForObservation(cr) {
return FailedObserve(), errors.New(errObjectNotFound)
}
requestDetails, err := c.requestDetails(cr, http.MethodGet)
if err != nil {
return FailedObserve(), err
}
details, responseErr := c.http.SendRequest(ctx, http.MethodGet, requestDetails.Url, requestDetails.Body, requestDetails.Headers, cr.Spec.ForProvider.InsecureSkipTLSVerify)
if details.HttpResponse.StatusCode == http.StatusNotFound {
return FailedObserve(), errors.New(errObjectNotFound)
}
desiredState, err := c.desiredState(cr)
if err != nil {
return FailedObserve(), err
}
return c.compareResponseAndDesiredState(details, responseErr, desiredState)
}
func (c *external) isObjectValidForObservation(cr *v1alpha1.Request) bool {
return cr.Status.Response.Body != "" &&
!(cr.Status.RequestDetails.Method == http.MethodPost && utils.IsHTTPError(cr.Status.Response.StatusCode))
}
func (c *external) compareResponseAndDesiredState(details httpClient.HttpDetails, err error, desiredState string) (ObserveRequestDetails, error) {
observeRequestDetails := NewObserve(details, err, false)
if json.IsJSONString(details.HttpResponse.Body) && json.IsJSONString(desiredState) {
responseBodyMap := json.JsonStringToMap(details.HttpResponse.Body)
desiredStateMap := json.JsonStringToMap(desiredState)
observeRequestDetails.Synced = json.Contains(responseBodyMap, desiredStateMap) && utils.IsHTTPSuccess(details.HttpResponse.StatusCode)
return observeRequestDetails, nil
}
if !json.IsJSONString(details.HttpResponse.Body) && json.IsJSONString(desiredState) {
return FailedObserve(), errors.Errorf(errNotValidJSON, "response body", details.HttpResponse.Body)
}
if json.IsJSONString(details.HttpResponse.Body) && !json.IsJSONString(desiredState) {
return FailedObserve(), errors.Errorf(errNotValidJSON, "PUT mapping result", desiredState)
}
observeRequestDetails.Synced = strings.Contains(details.HttpResponse.Body, desiredState) && utils.IsHTTPSuccess(details.HttpResponse.StatusCode)
return observeRequestDetails, nil
}
func (c *external) desiredState(cr *v1alpha1.Request) (string, error) {
requestDetails, err := c.requestDetails(cr, http.MethodPut)
return requestDetails.Body, err
}
func (c *external) requestDetails(cr *v1alpha1.Request, method string) (requestgen.RequestDetails, error) {
mapping, ok := getMappingByMethod(&cr.Spec.ForProvider, method)
if !ok {
return requestgen.RequestDetails{}, errors.Errorf(errMappingNotFound, method)
}
return generateValidRequestDetails(cr, mapping)
}