fix Incorrectly generated query strings (that rely on + not being urlencoded)

In the Kubernetes API client, we should be able to generate queries like
    ?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29
    (from https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering).
    When the generated URIs URL-encode the plus signs, the API server rejects them.

    With this change, (newConfig :: IO KubernetesClientConfig) now sets
    the default configQueryExtraUnreserved setting to "+". Requests
    generated with this config object will avoid URI encoding "+" all
    querystring params.
This commit is contained in:
Jon Schoning
2021-09-20 12:03:07 -05:00
parent edfb4744a4
commit 541fb76466
11 changed files with 120 additions and 16 deletions

View File

@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: kubernetes-client
version: 0.4.1.0
version: 0.4.2.0
synopsis: Client library for Kubernetes
description: Client library for interacting with a Kubernetes cluster.
.
@@ -60,7 +60,7 @@ library
, http-client-tls >=0.3
, jose-jwt >=0.8
, jsonpath >=0.1 && <0.3
, kubernetes-client-core ==0.4.1.0
, kubernetes-client-core ==0.4.2.0
, microlens >=0.4
, mtl >=2.2
, oidc-client >=0.4
@@ -105,7 +105,7 @@ test-suite example
, jose-jwt >=0.8
, jsonpath >=0.1 && <0.3
, kubernetes-client
, kubernetes-client-core ==0.4.1.0
, kubernetes-client-core ==0.4.2.0
, microlens >=0.4
, mtl >=2.2
, oidc-client >=0.4
@@ -158,7 +158,7 @@ test-suite spec
, jose-jwt >=0.8
, jsonpath >=0.1 && <0.3
, kubernetes-client
, kubernetes-client-core ==0.4.1.0
, kubernetes-client-core ==0.4.2.0
, microlens >=0.4
, mtl >=2.2
, oidc-client >=0.4

View File

@@ -1,5 +1,5 @@
name: kubernetes-client
version: 0.4.1.0
version: 0.4.2.0
description: |
Client library for interacting with a Kubernetes cluster.
@@ -49,7 +49,7 @@ dependencies:
- http-client >=0.5 && <0.8
- http-client-tls >=0.3
- jose-jwt >=0.8
- kubernetes-client-core ==0.4.1.0
- kubernetes-client-core ==0.4.2.0
- microlens >=0.4
- mtl >=2.2
- oidc-client >=0.4

View File

@@ -1,2 +1,2 @@
Requested Commit: df877a8cf9b5e2b7a02c6e88dc4767b1e25c0b6f
Actual Commit: df877a8cf9b5e2b7a02c6e88dc4767b1e25c0b6f
Requested Commit: 1247e774530b715fb54f719a3b10000d5dd2137b
Actual Commit: 1247e774530b715fb54f719a3b10000d5dd2137b

View File

@@ -1,5 +1,5 @@
name: kubernetes-client-core
version: 0.4.1.0
version: 0.4.2.0
synopsis: Auto-generated kubernetes-client-core API Client
description: .
Client library for calling the Kubernetes API based on http-client.

View File

@@ -550,6 +550,11 @@ instance HasOptionalParam ListClusterCustomObject Pretty where
applyOptionalParam req (Pretty xs) =
req `addQuery` toQuery ("pretty", Just xs)
-- | /Optional Param/ "allowWatchBookmarks" - allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.
instance HasOptionalParam ListClusterCustomObject AllowWatchBookmarks where
applyOptionalParam req (AllowWatchBookmarks xs) =
req `addQuery` toQuery ("allowWatchBookmarks", Just xs)
-- | /Optional Param/ "continue" - The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\". This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.
instance HasOptionalParam ListClusterCustomObject Continue where
applyOptionalParam req (Continue xs) =
@@ -575,6 +580,11 @@ instance HasOptionalParam ListClusterCustomObject ResourceVersion where
applyOptionalParam req (ResourceVersion xs) =
req `addQuery` toQuery ("resourceVersion", Just xs)
-- | /Optional Param/ "resourceVersionMatch" - resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. Defaults to unset
instance HasOptionalParam ListClusterCustomObject ResourceVersionMatch where
applyOptionalParam req (ResourceVersionMatch xs) =
req `addQuery` toQuery ("resourceVersionMatch", Just xs)
-- | /Optional Param/ "timeoutSeconds" - Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.
instance HasOptionalParam ListClusterCustomObject TimeoutSeconds where
applyOptionalParam req (TimeoutSeconds xs) =
@@ -616,6 +626,11 @@ instance HasOptionalParam ListNamespacedCustomObject Pretty where
applyOptionalParam req (Pretty xs) =
req `addQuery` toQuery ("pretty", Just xs)
-- | /Optional Param/ "allowWatchBookmarks" - allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.
instance HasOptionalParam ListNamespacedCustomObject AllowWatchBookmarks where
applyOptionalParam req (AllowWatchBookmarks xs) =
req `addQuery` toQuery ("allowWatchBookmarks", Just xs)
-- | /Optional Param/ "continue" - The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\". This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.
instance HasOptionalParam ListNamespacedCustomObject Continue where
applyOptionalParam req (Continue xs) =
@@ -641,6 +656,11 @@ instance HasOptionalParam ListNamespacedCustomObject ResourceVersion where
applyOptionalParam req (ResourceVersion xs) =
req `addQuery` toQuery ("resourceVersion", Just xs)
-- | /Optional Param/ "resourceVersionMatch" - resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details. Defaults to unset
instance HasOptionalParam ListNamespacedCustomObject ResourceVersionMatch where
applyOptionalParam req (ResourceVersionMatch xs) =
req `addQuery` toQuery ("resourceVersionMatch", Just xs)
-- | /Optional Param/ "timeoutSeconds" - Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.
instance HasOptionalParam ListNamespacedCustomObject TimeoutSeconds where
applyOptionalParam req (TimeoutSeconds xs) =

View File

@@ -32,6 +32,7 @@ import qualified Control.Exception.Safe as E
import qualified Control.Monad.IO.Class as P
import qualified Control.Monad as P
import qualified Data.Aeson.Types as A
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BCL
@@ -179,13 +180,18 @@ _toInitRequest config req0 =
(configValidateAuthMethods config && (not . null . rAuthTypes) req1)
(E.throw $ AuthMethodException $ "AuthMethod not configured: " <> (show . head . rAuthTypes) req1)
let req2 = req1 & _setContentTypeHeader & _setAcceptHeader
reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders (rParams req2)
reqQuery = NH.renderQuery True (paramsQuery (rParams req2))
pReq = parsedReq { NH.method = (rMethod req2)
params = rParams req2
reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders params
reqQuery = let query = paramsQuery params
queryExtraUnreserved = configQueryExtraUnreserved config
in if B.null queryExtraUnreserved
then NH.renderQuery True query
else NH.renderQueryPartialEscape True (toPartialEscapeQuery queryExtraUnreserved query)
pReq = parsedReq { NH.method = rMethod req2
, NH.requestHeaders = reqHeaders
, NH.queryString = reqQuery
}
outReq <- case paramsBody (rParams req2) of
outReq <- case paramsBody params of
ParamBodyNone -> pure (pReq { NH.requestBody = mempty })
ParamBodyB bs -> pure (pReq { NH.requestBody = NH.RequestBodyBS bs })
ParamBodyBL bl -> pure (pReq { NH.requestBody = NH.RequestBodyLBS bl })

View File

@@ -67,7 +67,7 @@ import Data.Function ((&))
import Data.Foldable(foldlM)
import Data.Monoid ((<>))
import Data.Text (Text)
import Prelude (($), (.), (<$>), (<*>), Maybe(..), Bool(..), Char, String, fmap, mempty, pure, return, show, IO, Monad, Functor)
import Prelude (($), (.), (&&), (<$>), (<*>), Maybe(..), Bool(..), Char, String, fmap, mempty, pure, return, show, IO, Monad, Functor, maybe)
-- * KubernetesClientConfig
@@ -79,6 +79,7 @@ data KubernetesClientConfig = KubernetesClientConfig
, configLogContext :: LogContext -- ^ Configures the logger
, configAuthMethods :: [AnyAuthMethod] -- ^ List of configured auth methods
, configValidateAuthMethods :: Bool -- ^ throw exceptions if auth methods are not configured
, configQueryExtraUnreserved :: B.ByteString -- ^ Configures additional querystring characters which must not be URI encoded, e.g. '+' or ':'
}
-- | display the config
@@ -109,6 +110,7 @@ newConfig = do
, configLogContext = logCxt
, configAuthMethods = []
, configValidateAuthMethods = True
, configQueryExtraUnreserved = "+"
}
-- | updates config use AuthMethod on matching requests
@@ -336,6 +338,16 @@ toQuery :: WH.ToHttpApiData a => (BC.ByteString, Maybe a) -> [NH.QueryItem]
toQuery x = [(fmap . fmap) toQueryParam x]
where toQueryParam = T.encodeUtf8 . WH.toQueryParam
toPartialEscapeQuery :: B.ByteString -> NH.Query -> NH.PartialEscapeQuery
toPartialEscapeQuery extraUnreserved query = fmap (\(k, v) -> (k, maybe [] go v)) query
where go :: B.ByteString -> [NH.EscapeItem]
go v = v & B.groupBy (\a b -> a `B.notElem` extraUnreserved && b `B.notElem` extraUnreserved)
& fmap (\xs -> if B.null xs then NH.QN xs
else if B.head xs `B.elem` extraUnreserved
then NH.QN xs -- Not Encoded
else NH.QE xs -- Encoded
)
-- *** OpenAPI `CollectionFormat` Utils
-- | Determines the format of the array if type array is used.

View File

@@ -68576,6 +68576,17 @@ paths:
required: true
schema:
type: string
- description: allowWatchBookmarks requests watch events with type "BOOKMARK".
Servers that do not implement bookmarks may ignore this flag and bookmarks
are sent at the server's discretion. Clients should not assume bookmarks
are returned at any specific interval, nor may they assume the server will
send any BOOKMARK event during a session. If this is not a watch, this field
is ignored. If the feature gate WatchBookmarks is not enabled in apiserver,
this field is ignored.
in: query
name: allowWatchBookmarks
schema:
type: boolean
- description: |-
The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
@@ -68614,6 +68625,14 @@ paths:
name: resourceVersion
schema:
type: string
- description: |-
resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
Defaults to unset
in: query
name: resourceVersionMatch
schema:
type: string
- description: Timeout for the list/watch call. This limits the duration of
the call, regardless of any activity or inactivity.
in: query
@@ -68819,6 +68838,17 @@ paths:
required: true
schema:
type: string
- description: allowWatchBookmarks requests watch events with type "BOOKMARK".
Servers that do not implement bookmarks may ignore this flag and bookmarks
are sent at the server's discretion. Clients should not assume bookmarks
are returned at any specific interval, nor may they assume the server will
send any BOOKMARK event during a session. If this is not a watch, this field
is ignored. If the feature gate WatchBookmarks is not enabled in apiserver,
this field is ignored.
in: query
name: allowWatchBookmarks
schema:
type: boolean
- description: |-
The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
@@ -68857,6 +68887,14 @@ paths:
name: resourceVersion
schema:
type: string
- description: |-
resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
Defaults to unset
in: query
name: resourceVersionMatch
schema:
type: string
- description: Timeout for the list/watch call. This limits the duration of
the call, regardless of any activity or inactivity.
in: query

View File

@@ -1,4 +1,4 @@
resolver: lts-14.7
resolver: lts-18.6
build:
haddock-arguments:
haddock-args:

View File

@@ -102276,6 +102276,13 @@
],
"description": "list or watch namespace scoped custom objects",
"parameters": [
{
"uniqueItems": true,
"type": "boolean",
"description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.",
"name": "allowWatchBookmarks",
"in": "query"
},
{
"uniqueItems": true,
"in": "query",
@@ -102311,6 +102318,13 @@
"description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
"name": "resourceVersion"
},
{
"uniqueItems": true,
"type": "string",
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"name": "resourceVersionMatch",
"in": "query"
},
{
"uniqueItems": true,
"in": "query",
@@ -102502,6 +102516,13 @@
],
"description": "list or watch cluster scoped custom objects",
"parameters": [
{
"uniqueItems": true,
"type": "boolean",
"description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.",
"name": "allowWatchBookmarks",
"in": "query"
},
{
"uniqueItems": true,
"in": "query",
@@ -102537,6 +102558,13 @@
"description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
"name": "resourceVersion"
},
{
"uniqueItems": true,
"type": "string",
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"name": "resourceVersionMatch",
"in": "query"
},
{
"uniqueItems": true,
"in": "query",

View File

@@ -26,4 +26,4 @@ export PACKAGE_NAME="kubernetes"
export USERNAME="kubernetes"
OPENAPI_GENERATOR_COMMIT=df877a8cf9b5e2b7a02c6e88dc4767b1e25c0b6f
OPENAPI_GENERATOR_COMMIT=1247e774530b715fb54f719a3b10000d5dd2137b