Skip to content

Commit e49e699

Browse files
committed
🚧 Work in progress: Factor out binding-request-parsing.
1 parent 2c95f0d commit e49e699

File tree

2 files changed

+147
-4
lines changed

2 files changed

+147
-4
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package bindingrequestparser
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
"code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/policyvalidator"
9+
"code.cloudfoundry.org/app-autoscaler/src/autoscaler/models"
10+
"code.cloudfoundry.org/brokerapi/v13/domain"
11+
"code.cloudfoundry.org/brokerapi/v13/domain/apiresponses"
12+
"code.cloudfoundry.org/lager/v3"
13+
)
14+
15+
type BindRequestParser = interface {
16+
Parse(instanceID string, details domain.BindDetails) (models.AppScalingConfig, error)
17+
}
18+
19+
type bindRequestParser struct {
20+
policyValidator *policyvalidator.PolicyValidator
21+
}
22+
23+
var _ BindRequestParser = &bindRequestParser{
24+
policyValidator: policyvalidator.NewPolicyValidator(
25+
"🚸 This is a dummy and never executed!", 0, 0, 0, 0, 0, 0, 0, 0,
26+
)}
27+
28+
func (brp *bindRequestParser) Parse(instanceID string, details domain.BindDetails) (models.AppScalingConfig, error) {
29+
var scalingPolicyRaw json.RawMessage
30+
if details.RawParameters != nil {
31+
scalingPolicyRaw = details.RawParameters
32+
}
33+
34+
// This just gets used for legacy-reasons. The actually parsing happens in the step
35+
// afterwards. But it still does not validate against the schema, which is done here.
36+
_, err := brp.getPolicyFromJsonRawMessage(scalingPolicyRaw, instanceID, details.PlanID)
37+
if err != nil {
38+
err := fmt.Errorf("validation-error against the json-schema:\n\t%w", err)
39+
return models.AppScalingConfig{}, err
40+
}
41+
42+
scalingPolicy, err := models.ScalingPolicyFromRawJSON(scalingPolicyRaw)
43+
if err != nil {
44+
err := fmt.Errorf("could not parse scaling policy from request:\n\t%w", err)
45+
return models.AppScalingConfig{}, err
46+
// // ⚠️ I need to be run on the receiver-side.
47+
// return nil, apiresponses.NewFailureResponseBuilder(
48+
// ErrInvalidConfigurations, http.StatusBadRequest, actionReadScalingPolicy).
49+
// WithErrorKey(actionReadScalingPolicy).
50+
// Build()
51+
}
52+
53+
// // 🚧 To-do: Check if exactly one is provided. We don't want to accept both to be present.
54+
// requestAppGuid := details.BindResource.AppGuid
55+
// paramsAppGuid := bindingConfig.Configuration.AppGUID
56+
var appGUID string
57+
if details.BindResource != nil && details.BindResource.AppGuid != "" {
58+
appGUID = details.BindResource.AppGuid
59+
} else if details.AppGUID != "" {
60+
// 👎 Access to `details.AppGUID` has been deprecated, see:
61+
// <https://github.com/openservicebrokerapi/servicebroker/blob/v2.17/spec.md#request-creating-a-service-binding>
62+
appGUID = details.AppGUID
63+
} else {
64+
// 🚧 To-do: Implement feature: service-key-creation; Use appID from `bindingConfig`!
65+
}
66+
67+
if appGUID == "" {
68+
err := errors.New("error: service must be bound to an application - service key creation is not supported")
69+
logger.Error("check-required-app-guid", err)
70+
return result, apiresponses.NewFailureResponseBuilder(
71+
err, http.StatusUnprocessableEntity, "check-required-app-guid").
72+
WithErrorKey("RequiresApp").Build()
73+
}
74+
75+
// 💡🚧 To-do: We should fail during startup if this does not work. Because then the
76+
// configuration of the service is corrupted.
77+
var defaultCustomMetricsCredentialType *models.CustomMetricsBindingAuthScheme
78+
defaultCustomMetricsCredentialType, err = models.ParseCustomMetricsBindingAuthScheme(
79+
b.conf.DefaultCustomMetricsCredentialType)
80+
if err != nil {
81+
programmingError := &models.InvalidArgumentError{
82+
Param: "default-credential-type",
83+
Value: b.conf.DefaultCustomMetricsCredentialType,
84+
Msg: "error parsing default credential type",
85+
}
86+
logger.Error("parse-default-credential-type", programmingError,
87+
lager.Data{
88+
"default-credential-type": b.conf.DefaultCustomMetricsCredentialType,
89+
})
90+
return result, apiresponses.NewFailureResponse(err, http.StatusInternalServerError,
91+
"parse-default-credential-type")
92+
}
93+
// 🏚️ Subsequently we assume that this credential-type-configuration is part of the
94+
// scaling-policy and check it accordingly. However this is legacy and not in line with the
95+
// current terminology of “PolicyDefinition”, “ScalingPolicy”, “BindingConfig” and
96+
// “AppScalingConfig”.
97+
customMetricsBindingAuthScheme, err := getOrDefaultCredentialType(scalingPolicyRaw,
98+
defaultCustomMetricsCredentialType, logger)
99+
if err != nil {
100+
return result, err
101+
}
102+
103+
// To-do: 🚧 Factor everything that is involved in this creation out into an own
104+
// helper-function. Consider a function analogous to `getScalingPolicyFromRequest` that is
105+
// defined within this file.
106+
appScalingConfig := models.NewAppScalingConfig(
107+
*models.NewBindingConfig(models.GUID(appGUID), customMetricsBindingAuthScheme),
108+
*scalingPolicy)
109+
110+
return models.AppScalingConfig{}, models.ErrUnimplemented
111+
}
112+
113+
func (brp *bindRequestParser) getPolicyFromJsonRawMessage(policyJson json.RawMessage, instanceID string, planID string) (*models.ScalingPolicy, error) {
114+
if isEmptyPolicy := len(policyJson) <= 0; isEmptyPolicy { // no nil-check needed: `len(nil) == 0`
115+
return nil, nil
116+
}
117+
118+
policy, errResults := brp.policyValidator.ParseAndValidatePolicy(policyJson)
119+
if errResults != nil {
120+
// 🚫 The subsequent log-message is a strong assumption about the context of the caller. But
121+
// how can we actually know here that we operate on a default-policy? In fact, when we are
122+
// in the call-stack of `Bind` then we are *not* called with a default-policy.
123+
resultsJson, err := json.Marshal(errResults)
124+
if err != nil {
125+
return nil, &models.InvalidArgumentError{
126+
Param: "errResults",
127+
Value: errResults,
128+
Msg: "Failed to json-marshal validation results; This should never happen."}
129+
}
130+
return policy, apiresponses.NewFailureResponse(fmt.Errorf("invalid policy provided: %s", string(resultsJson)), http.StatusBadRequest, "failed-to-validate-policy")
131+
}
132+
133+
return policy, nil
134+
}
135+
136+
func (brp *bindRequestParser) getScalingPolicyFromRequest(
137+
scalingPolicyRaw json.RawMessage, logger lager.Logger,
138+
) (*models.ScalingPolicy, error) {
139+
}

src/autoscaler/api/broker/broker.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,7 @@ func (b *Broker) validateAndCheckPolicy(rawJson json.RawMessage, instanceID stri
205205
}
206206
return policy, apiresponses.NewFailureResponse(fmt.Errorf("invalid policy provided: %s", string(resultsJson)), http.StatusBadRequest, "failed-to-validate-policy")
207207
}
208-
if err := b.planDefinitionExceeded(policy.GetPolicyDefinition(), planID, instanceID); err != nil {
209-
return policy, err
210-
}
208+
211209
return policy, nil
212210
}
213211

@@ -533,7 +531,13 @@ func (b *Broker) Bind(
533531
logger.Error("get-scaling-policy-configuration-from-request", err)
534532
return result, err
535533
}
536-
policyGuidStr := uuid.NewString()
534+
535+
// ⚠️ I need to stay here after factorising out the request-parsing!
536+
if err := b.planDefinitionExceeded(scalingPolicy.GetPolicyDefinition(), details.PlanID, instanceID); err != nil {
537+
return result, err
538+
}
539+
540+
policyGuidStr := uuid.NewString() // ⚠️ I need to stay here after factorising out the request-parsing!
537541

538542
// // 🚧 To-do: Check if exactly one is provided. We don't want to accept both to be present.
539543
// requestAppGuid := details.BindResource.AppGuid

0 commit comments

Comments
 (0)