Closed akutz closed 4 years ago
FWIW, I've got a working Golang example of creating and testing an LB with the dataplane API. It starts at https://github.com/akutz/cluster-api-provider-vsphere/blob/feature/haproxy/pkg/haproxy/haproxy_test.go.
Hi, try generating a client using this: https://github.com/go-swagger/go-swagger
We use it to generate both the API and the github.com/haproxytech/models package. With the client lib there you can use the already existing models.
Hi @mjuraga,
Thank you for this. I didn't see the models
package, nice. However, in trying to use go-swagger, it's complaining that the OpenAPI spec isn't valid. Is there some other spec file I should be using?
Hi @mjuraga,
Nevermind, I found it :) https://github.com/haproxytech/dataplaneapi-specification
Hi @mjuraga,
Whether I use Go-Swagger's latest release (v0.21.0
) or v0.19.0
(as indicated at https://github.com/haproxytech/models), I receive the following errors with the spec:
$ docker run --rm -it \
> -v "$(pwd)":/build quay.io/goswagger/swagger:v0.21.0 \
> validate \
> https://raw.githubusercontent.com/haproxytech/dataplaneapi-specification/v1.2.4/haproxy-spec.yaml
2020/01/04 15:37:48
The swagger spec at "https://raw.githubusercontent.com/haproxytech/dataplaneapi-specification/v1.2.4/haproxy-spec.yaml" showed up some valid but possibly unwanted constructs.
2020/01/04 15:37:48 See warnings below:
2020/01/04 15:37:48 - WARNING: parameter "#/parameters/force_reload" is not used anywhere
2020/01/04 15:37:48 - WARNING: parameter "#/parameters/transaction_id" is not used anywhere
2020/01/04 15:37:48 - WARNING: parameter "#/parameters/version" is not used anywhere
2020/01/04 15:37:48 - WARNING: response "#/responses/BadRequest" is not used anywhere
2020/01/04 15:37:48 - WARNING: response "#/responses/NotFound" is not used anywhere
2020/01/04 15:37:48 - WARNING: response "#/responses/AlreadyExists" is not used anywhere
2020/01/04 15:37:48 - WARNING: response "#/responses/DefaultError" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/acls" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/filters" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/default_server" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/http_request_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/backend_switching_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/tcp_response_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/frontends" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/sites" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/reloads" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/process_info" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/native_stat" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/redispatch" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/defaults" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/tcp_request_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/global" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/http_response_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/backends" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/stick_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/native_stats" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/httpchk" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/balance" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/errorfile" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/forwardfor" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/endpoints" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/server_switching_rules" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/servers" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/log_targets" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/transactions" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/binds" is not used anywhere
2020/01/04 15:37:48 - WARNING: definition "#/definitions/info" is not used anywhere
The swagger spec at "https://raw.githubusercontent.com/haproxytech/dataplaneapi-specification/v1.2.4/haproxy-spec.yaml" is invalid against swagger specification 2.0.
See errors below:
- invalid ref "paths/haproxy.yaml#/filters"
- invalid ref "models/general.yaml#/process_info"
- invalid ref "models/haproxy.yaml#/bind"
- invalid ref "paths/haproxy.yaml#/defaults"
- invalid ref "paths/haproxy.yaml#/tcp_response_rules"
- invalid ref "models/general.yaml#/transaction"
- invalid ref "models/stats.yaml#/native"
- invalid ref "models/haproxy.yaml#/acl"
- invalid ref "models/haproxy.yaml#/global"
- invalid ref "models/haproxy.yaml#/httpchk"
- invalid ref "paths/haproxy.yaml#/frontends_one"
- invalid ref "paths/general.yaml#/specification"
- invalid ref "paths/general.yaml#/info"
- invalid ref "paths/simple.yaml#/sites_one"
- invalid ref "paths/haproxy.yaml#/backend_switching_rules"
- invalid ref "paths/general.yaml#/stats"
- invalid ref "paths/haproxy.yaml#/stick_rules"
- invalid ref "paths/stats.yaml#/native"
- invalid ref "paths/general.yaml#/configuration"
- invalid ref "paths/general.yaml#/reloads_one"
- invalid ref "models/haproxy.yaml#/filter"
- invalid ref "paths/general.yaml#/haproxy"
- invalid ref "paths/haproxy.yaml#/backends"
- invalid ref "models/general.yaml#/info"
- invalid ref "models/general.yaml#/reload"
- invalid ref "paths/haproxy.yaml#/http_request_rules_one"
- invalid ref "models/haproxy.yaml#/server_switching_rule"
- invalid ref "models/simple.yaml#/site"
- invalid ref "models/stats.yaml#/native_stats"
- invalid ref "paths/haproxy.yaml#/acls"
- invalid ref "paths/haproxy.yaml#/server_switching_rules"
- invalid ref "paths/general.yaml#/root"
- invalid ref "paths/haproxy.yaml#/stick_rules_one"
- invalid ref "paths/haproxy.yaml#/tcp_request_rules_one"
- invalid ref "paths/general.yaml#/transactions_one"
- invalid ref "models/haproxy.yaml#/backend_switching_rule"
- invalid ref "models/haproxy.yaml#/tcp_response_rule"
- invalid ref "paths/haproxy.yaml#/tcp_request_rules"
- invalid ref "paths/haproxy.yaml#/log_targets_one"
- invalid ref "paths/haproxy.yaml#/log_targets"
- invalid ref "models/haproxy.yaml#/http_response_rule"
- invalid ref "models/haproxy.yaml#/log_target"
- invalid ref "models/errors.yaml#/error"
- invalid ref "models/haproxy.yaml#/default_server"
- invalid ref "paths/haproxy.yaml#/tcp_response_rules_one"
- invalid ref "paths/haproxy.yaml#/frontends"
- invalid ref "paths/general.yaml#/reloads"
- invalid ref "paths/haproxy.yaml#/http_response_rules"
- invalid ref "models/haproxy.yaml#/http_request_rule"
- invalid ref "models/haproxy.yaml#/stick_rule"
- invalid ref "paths/general.yaml#/transactions"
- invalid ref "paths/haproxy.yaml#/server_switching_rules_one"
- invalid ref "paths/haproxy.yaml#/backend_switching_rules_one"
- invalid ref "models/haproxy.yaml#/frontend"
- invalid ref "paths/haproxy.yaml#/acls_one"
- invalid ref "paths/haproxy.yaml#/backends_one"
- invalid ref "paths/haproxy.yaml#/servers_one"
- invalid ref "models/haproxy.yaml#/balance"
- invalid ref "paths/haproxy.yaml#/filters_one"
- invalid ref "models/haproxy.yaml#/server"
- invalid ref "paths/haproxy.yaml#/binds"
- invalid ref "paths/haproxy.yaml#/http_response_rules_one"
- invalid ref "paths/general.yaml#/services"
- invalid ref "models/haproxy.yaml#/backend"
- invalid ref "models/haproxy.yaml#/errorfile"
- invalid ref "paths/haproxy.yaml#/servers"
- invalid ref "paths/haproxy.yaml#/global"
- invalid ref "models/haproxy.yaml#/tcp_request_rule"
- invalid ref "models/haproxy.yaml#/defaults"
- invalid ref "paths/haproxy.yaml#/configuration"
- invalid ref "paths/general.yaml#/process_info"
- invalid ref "paths/haproxy.yaml#/binds_one"
- invalid ref "models/haproxy.yaml#/redispatch"
- invalid ref "paths/simple.yaml#/sites"
- invalid ref "models/general.yaml#/endpoint"
- invalid ref "paths/haproxy.yaml#/http_request_rules"
- invalid ref "models/haproxy.yaml#/forwardfor"
The same errors occur with Go-Swagger v0.19.0
.
Now this is interesting. Downloading the spec first and then running the validate command works (with warnings):
$ docker run --rm -it -v "/Users/akutz/Downloads":/build quay.io/goswagger/swagger:v0.21.0 validate /build/haproxy_spec.yaml
2020/01/04 15:41:52
The swagger spec at "/build/haproxy_spec.yaml" is valid against swagger specification 2.0
2020/01/04 15:41:52
The swagger spec at "/build/haproxy_spec.yaml" showed up some valid but possibly unwanted constructs.
2020/01/04 15:41:52 See warnings below:
2020/01/04 15:41:52 - WARNING: example value for data in body does not validate its schema
2020/01/04 15:41:52 - WARNING: data.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: in operation "createTCPRequestRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: in operation "createTCPRequestRule", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: data.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: in operation "createTCPResponseRule", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: in operation "createTCPResponseRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: data.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: in operation "createStickRule", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: in operation "createStickRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: data.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: data.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: data.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: in operation "createBackend", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: 201.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: 201.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: in operation "createBackend", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: 202.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: 202.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: data.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "createServer", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "createServer", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: data.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "createSite", example value in response 201 does not validate its schema
2020/01/04 15:41:52 - WARNING: 201.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "createSite", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: 202.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "getServer", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "getTCPRequestRules", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: in operation "getSites", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "getServers", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "getStickRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: in operation "getBackends", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: in operation "getTCPResponseRules", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: in operation "getTCPResponseRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: in operation "getTCPRequestRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: in operation "getStickRules", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.items.example.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: in operation "getBackend", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: 200.data.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: 200.data.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: in operation "getSite", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.data.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "replaceStickRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.example.type in body should be one of [match on store-request store-response]
2020/01/04 15:41:52 - WARNING: in operation "replaceStickRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceTCPResponseRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: in operation "replaceTCPResponseRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceBackend", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: 200.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: 200.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: in operation "replaceBackend", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceTCPRequestRule", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: in operation "replaceTCPRequestRule", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceServer", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: in operation "replaceServer", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceSite", example value in response 202 does not validate its schema
2020/01/04 15:41:52 - WARNING: in operation "replaceSite", example value in response 200 does not validate its schema
2020/01/04 15:41:52 - WARNING: 200.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: definitions.tcp_response_rule.example.type in body should be one of [content inspect-delay]
2020/01/04 15:41:52 - WARNING: definitions.site.farms.items.example.servers.items.example.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: definitions.server.example.max-connections in body is a forbidden property
2020/01/04 15:41:52 - WARNING: definitions.tcp_request_rule.example.type in body should be one of [connection content inspect-delay session]
2020/01/04 15:41:52 - WARNING: definitions.backend.example.httpchk.method in body should be one of [HEAD PUT POST GET TRACE PATCH]
2020/01/04 15:41:52 - WARNING: definitions.backend.example.forwardfor.enabled in body must be of type string: "boolean"
2020/01/04 15:41:52 - WARNING: definitions.backend.example.forwardfor.enabled in body should be one of [enabled]
2020/01/04 15:41:52 - WARNING: definitions.stick_rule.example.type in body should be one of [match on store-request store-response]
After finally getting the Go-Swagger client generated, it's as I remember, Go-Swagger-generated code is...poorly. I prefer the OpenAPI-generated client, even with its issues. It would be nice to get those fixed.
FWIW, the CAPV PR merged using the OpenAPI bindings. They work fine :) https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/pull/705
I found what is the issue. It's the way openapi-generator for go handles tags. It creates an api_* file/service for every tag, and in our specification we have multiple tags for some endpoints, for documentation purposes, so these files are created and are surplus:
In go-swagger you can define a list of tags you want to generate, so we avoid generating those "documentation" tags. I didn't find a way to generate only specific tags in openapi-generator. When removing tags "Frotend options", "Backend options" and "HAProxy configuration management" tags, this solves the first issue of duplicate structs.
Maybe we can remove the extra tags from spec, to be inline with all mostly used generators. I just need to check other implications of that.
I am now looking into the issue of pointers to structs now.
The second issue is with how the openapi-generator generator client library handles nested objects. Some of those nested objects have required fields, and when they are marshaled into JSON, they are not omitted even if they have the json: omitempty
tag.
I think this is more of an issue with the generator then the API, they should have been pointers, so that they are omitted when marshaled into JSON. This way in JSON, you have an empty nested object, but since this object has required fields, the validation on server complains, because required fields are not set.
The examples at https://www.haproxy.com/documentation/hapee/1-9r1/configuration/dataplaneapi/ are incompatible with the Golang client bindings generated from the OpenAPI spec. Aside from having to create a program to remove the duplicate
Opt
structs, I've also had to do yet some more post-processing to make theBackend
model compatible with the aforementioned examples:If those fields aren't pointers, then you end up with obtuse errors (when using transactions) as a result of trying to create a new backend. While the values of
Forwardfor.Enabled
,HashType.Method
,BackendStickTable.Type
, andBackendStickTable.Size
may all be tagged asomitempty
, the server disagrees when empty values are submitted for the struct (and thus its fields).I had to do the following to get a backend successfully created in Golang, and I'm not even sure if those values are the defaults for those fields.