• Uncategorized

About powershell : kubectl-patch-works-on-Linux-Bash-but-not-in-Windows-Powershell-ISE

Question Detail

The following command works fine on Ubuntu bash:

kubectl patch deployment wapi-backend-d1 –patch ‘{“spec”: {“template”: {“metadata”: {“labels”: {“date”: “test”}}}}}’

The same command does not work in Windows Powershell Console (ISE).

The error is:

kubectl : Error from server (BadRequest): invalid character ‘s’ looking for beginning of object key string
At line:1 char:1
+ kubectl patch deployment wapi-backend-d1 –patch ‘{“spec”: {“template …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Error from serv…ject key string:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError

The powershell console version is:

PS > $PSVersionTable

Name Value
—- —–
PSVersion 5.1.14409.1005
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
BuildVersion 10.0.14409.1005
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3

I have tried the command with a different patched value too as I saw somebody write that patch may fail if it is already applied.

The path /spec/template/metadata/labels/date indeed exists in the deployment’s yaml, so that isn’t a problem either.

I presume that it might have something to do with kubectl working differently in Powershell in relation to quotes, but could not find a way to make it work.

I have tried

kubectl patch deployment wapi-backend-d1 –patch “{\”spec\”: {\”template\”: {\”metadata\”: {\”labels\”: {\”date\”: \”test123\”}}}}}”

But that results in

Error from server (NotFound): deployments.extensions “spec\\: {\\template\\: {\\metadata\\: {\\labels\\: {\\date\\: \\test123\\}}}}}” not found

What should be the command on Powershell?

Question Answer

For detailed and very useful background, see the answer by mklement0

After much frustration, I have decided to list all variants of quote escaping that I’ve tried, and came up with one more, which suddenly worked!
So, sharing it here:

kubectl patch deployment wapi-backend-d1 –patch ‘{\”spec\”: {\”template\”: {\”metadata\”: {\”labels\”: {\”date\”: \”test123\”}}}}}’

This is how to use kubectl patch with Powershell

Also, of note: I was actually trying to patch it with a timestamp to trigger a rolling update without changing tags of container images (so set image would not help me).

When you try to put your JSON into a variable and then call kubectl patch with a variable, you get into trouble with escaping again. This is what I ended up with:

$patchRequest = @{
spec = @{
template = @{
metadata = @{
labels = @{
date = ((((Get-Date -Format o)).replace(‘:’,’-‘).replace(‘+’,’_’)))
$patchJson = ((ConvertTo-Json -InputObject $patchRequest -Compress -Depth 10))
$patchJson = $patchJson.replace(‘”‘,’\”‘)
kubectl patch deployment wapi-backend-d1 –patch $patchJson


You’ve found the right solution in your own answer, but let me try to break it down conceptually:
Embedding ” (double quotes) in string arguments passed to external programs:

(a) First – sensibly and unavoidably – you need to satisfy PowerShell’s syntax requirements with respect to embedding ” chars. in quoted strings.

(b) Then – and this step shouldn’t be necessary – you need to \-escape embedded ” chars. that you want external programs to see.

This is a longstanding, irksome bug present up to at least PowerShell 7.2, which may get fixed in 7.3 – see this answer.

Re (a), you have the following options:

‘…’-quoting (single-quoting), i.e. a verbatim string, inside of which you can use ” as-is:

‘{ “spec”: “none” }’
Everything inside ‘…’ is taken verbatim (literally) – no expansion (string interpolation) takes place.

“…”-quoting (double-quoting), i.e. an expandable string, inside of which you can use `” or “” to embed ” chars:

“{ `”spec`”: `”none`” }” – ` is PowerShell’s general escape char.
“{ “”spec””: “”none”” }” – “-specific escaping (doubling)
The content of “…” is subject to expansion (string interpolation), meaning that you can reference variables ($var) or subexpressions ($(1 + 2)) inside such strings, which PowerShell replaces with their values – see this answer for more about PowerShell’s expandable strings.

If you’re passing such a string to other PowerShell commands (cmdlets, functions, or scripts), no further action is needed; e.g.:
PS> Write-Output ‘3″ of rain’
3″ of rain

Re (b) – i.e. to pass such strings to external programs – you additionally need to \-escape the embedded ” chars.:

Applying manual escaping to the examples above:

‘{ \”spec\”: \”none\” }’
“{ \`”spec\`”: \`”none\`” }”
“{ \””spec\””: \””none\”” }”

Applying the escaping programmatically to a preexisting string:

Replace verbatim ” with verbatim \”, as well as any preexisting, immediately preceding \ with \\:

$str = ‘3″ of rain’; $escapedStr = $str -replace ‘([\\]*)”‘, ‘$1$1\”‘

That is, for an external program to ultimately see value 3″ of rain verbatim, you must pass literal value 3\” of rain from PowerShell. This \-escaping is something that PowerShell, as a shell, should do automatically behind the scenes, but currently doesn’t.

There’s an additional bug in Windows PowerShell – since fixed in PowerShell Core – that mishandles strings with unbalanced embedded ” chars. if a ” is part of the first word:

E.g., the above techniques do NOT work with literal values such as 3″ of rain; that is, escaping this as ‘3\” of rain’ does not work as expected – instead, you must use the following monstrosity: `”3\`” of rain`”, which is technically a series of separate, unquoted arguments, which means that (a) multiple spaces between the words of the strings aren’t supported (they are collapsed to a single space) and (b) PowerShell metacharacters such as & < > $ & | @ { must be individually `-escaped.
Note that the bug surfaces only if the ” is part of the first word in the value, and only if that first word is not preceded by whitespace (though arguments with leading whitespace are rarely useful); e.g., ‘3 \” of rain’ would again work, because the unbalanced ” is not part of the first word.

The following uses choice.exe as an example external program, because it can be repurposed (via options /d Y /t 0) to merely echo the prompt string it is given, which shows how it received the string passed from PowerShell:
& {
# Note: For preview versions of v7.2, deactivate the experimental
# feature that fixes the problem, so as to show the original problem.
$PSNativeCommandArgumentPassing = ‘Legacy’

# Use manual \-escaping to pass what should be received as
# verbatim { “spec”: “none” } to an external program.
choice /m ‘{ \”spec\”: \”none\” }’ /d Y /t 0

The above outputs { “spec”: “none” } [Y,N]?Y, showing that the manually escaped ” chars. were received as verbatim ” chars. by the external program.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.