kubectl patch can't be applied to ConfigMap data

Created on 27 Mar 2020  路  7Comments  路  Source: kubernetes/kubectl

What happened:

When I applied patch to ConfigMap, it doesn't work well. (Error from server: Invalid JSON Patch)

Command

$ kubectl patch configmap/cm-test-toshi -n dev --type merge -p '{
  "data":{
    "config":{
      "template":{
        "containers":[{
          "name":"istio-proxy",
          "lifecycle":{
            "preStop":{
              "exec":{
                "command":[\"/bin/sh\", \"-c\", \"while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0]; do sleep 1; done\"]
              }
            }
          }
        }]
      }
    }
  }
}'

Target CondigMap

apiVersion: v1
data:
  config: |-
    policy: disabled
    alwaysInjectSelector:
      []
    neverInjectSelector:
      []
    template: |
      initContainers:
      - name: istio-validation
      ...
      containers:
      - name: istio-proxy
      {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }}
        image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}"
      {{- else }}
        image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}"
      {{- end }}
        ports:
        ...

What you expected to happen:

No error occurs and data.condif.template.containers[].lifecycle.preStop.exec.command is applied.

apiVersion: v1
data:
  config: |-
    policy: disabled
    alwaysInjectSelector:
      []
    neverInjectSelector:
      []
    template: |
      initContainers:
      - name: istio-validation
      ...
      containers:
      containers:
      - name: istio-proxy
        lifecycle:     # added
          preStop:   # added
            exec:       # added
              command:  ["/bin/sh", "-c", "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do sleep 1; done"] # added
      {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }}
        image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}"
      {{- else }}
        image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}"
      {{- end }}
        ports:
        ...

How to reproduce it (as minimally and precisely as possible):

  1. Create ConfigMap
$ kubectl apply -f cm-test-toshi.yaml

cm-test-toshi.yaml

apiVersion: v1
data:
  config: |-
    policy: disabled
    alwaysInjectSelector:
      []
    neverInjectSelector:
      []
    template: |
      {{- $cniDisabled := (not .Values.istio_cni.enabled) }}
      {{- $cniRepairEnabled := (and .Values.istio_cni.enabled .Values.istio_cni.repair.enabled) }}
      {{- $enableInitContainer := (or $cniDisabled $cniRepairEnabled .Values.global.proxy.enableCoreDump) }}
      rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }}
      {{- if $enableInitContainer }}
      initContainers:
      {{- if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }}
      {{ if $cniRepairEnabled -}}
      - name: istio-validation
      {{ else -}}
      - name: istio-init
      {{ end -}}
      {{- if contains "/" .Values.global.proxy_init.image }}
        image: "{{ .Values.global.proxy_init.image }}"
      {{- else }}
        image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}"
      {{- end }}
        command:
      {{- if $cniRepairEnabled }}
        - istio-iptables-go
      {{- else }}
        - istio-iptables
      {{- end }}
        - "-p"
        - "15001"
        - "-z"
        - "15006"
        - "-u"
        - 1337
        - "-m"
        - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}"
        - "-i"
        - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}"
        - "-x"
        - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}"
        - "-b"
        - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}"
        - "-d"
        - "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}"
        {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}}
        - "-o"
        - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}"
        {{ end -}}
        {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}}
        - "-k"
        - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}"
        {{ end -}}
        {{ if $cniRepairEnabled -}}
        - "--run-validation"
        - "--skip-rule-apply"
        {{- end }}
        imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}"
      {{- if .Values.global.proxy_init.resources }}
        resources:
          {{ toYaml .Values.global.proxy_init.resources | indent 4 }}
      {{- else }}
        resources: {}
      {{- end }}
        securityContext:
          allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }}
          privileged: {{ .Values.global.proxy.privileged }}
          capabilities:
        {{- if not $cniRepairEnabled }}
            add:
            - NET_ADMIN
            - NET_RAW
        {{- end }}
            drop:
            - ALL
          readOnlyRootFilesystem: false
        {{- if not $cniRepairEnabled }}
          runAsGroup: 0
          runAsNonRoot: false
          runAsUser: 0
        {{- else }}
          runAsGroup: 1337
          runAsUser: 1337
          runAsNonRoot: true
        {{- end }}
        restartPolicy: Always
      {{ end -}}
      {{- if eq .Values.global.proxy.enableCoreDump true }}
      - name: enable-core-dump
        args:
        - -c
        - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited
        command:
          - /bin/sh
      {{- if contains "/" .Values.global.proxy_init.image }}
        image: "{{ .Values.global.proxy_init.image }}"
      {{- else }}
        image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}"
      {{- end }}
        imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}"
        resources: {}
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            add:
            - SYS_ADMIN
            drop:
            - ALL
          privileged: true
          readOnlyRootFilesystem: false
          runAsGroup: 0
          runAsNonRoot: false
          runAsUser: 0
      {{ end }}
      {{ end }}
      containers:
      - name: istio-proxy
      {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }}
        image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}"
      {{- else }}
        image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}"
      {{- end }}
        ports:
        - containerPort: 15090
          protocol: TCP
          name: http-envoy-prom
        args:
        - proxy
        - sidecar
        - --domain
        - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }}
        - --configPath
        - "/etc/istio/proxy"
        - --binaryPath
        - "/usr/local/bin/envoy"
        - --serviceCluster
        {{ if ne "" (index .ObjectMeta.Labels "app") -}}
        - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)"
        {{ else -}}
        - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}"
        {{ end -}}
        - --drainDuration
        - "{{ formatDuration .ProxyConfig.DrainDuration }}"
        - --parentShutdownDuration
        - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}"
        - --discoveryAddress
        - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}"
      {{- if eq .Values.global.proxy.tracer "lightstep" }}
        - --lightstepAddress
        - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}"
        - --lightstepAccessToken
        - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}"
        - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }}
        - --lightstepCacertPath
        - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}"
      {{- else if eq .Values.global.proxy.tracer "zipkin" }}
        - --zipkinAddress
        - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}"
      {{- else if eq .Values.global.proxy.tracer "datadog" }}
        - --datadogAgentAddress
        - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}"
      {{- end }}
        - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}}
        - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}}
        - --connectTimeout
        - "{{ formatDuration .ProxyConfig.ConnectTimeout }}"
      {{- if .Values.global.proxy.envoyStatsd.enabled }}
        - --statsdUdpAddress
        - "{{ .ProxyConfig.StatsdUdpAddress }}"
      {{- end }}
      {{- if .Values.global.proxy.envoyMetricsService.enabled }}
        - --envoyMetricsServiceAddress
        - "{{ .ProxyConfig.GetEnvoyMetricsService.GetAddress }}"
      {{- end }}
      {{- if .Values.global.proxy.envoyAccessLogService.enabled }}
        - --envoyAccessLogServiceAddress
        - "{{ .ProxyConfig.GetEnvoyAccessLogService.GetAddress }}"
      {{- end }}
        - --proxyAdminPort
        - "{{ .ProxyConfig.ProxyAdminPort }}"
        {{ if gt .ProxyConfig.Concurrency 0 -}}
        - --concurrency
        - "{{ .ProxyConfig.Concurrency }}"
        {{ end -}}
        {{- if .Values.global.controlPlaneSecurityEnabled }}
        - --controlPlaneAuthPolicy
        - MUTUAL_TLS
        {{- else }}
        - --controlPlaneAuthPolicy
        - NONE
        {{- end }}
        - --dnsRefreshRate
        - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }}
      {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }}
        - --statusPort
        - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}"
        - --applicationPorts
        - "{{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts` (applicationPorts .Spec.Containers) }}"

      {{- end }}
      {{- if .Values.global.trustDomain }}
        - --trust-domain={{ .Values.global.trustDomain }}
      {{- end }}
      {{- if .Values.global.logAsJson }}
        - --log_as_json
      {{- end }}
      {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }}
        - --templateFile=/etc/istio/custom-bootstrap/envoy_bootstrap.json
      {{- end }}
      {{- if .Values.global.proxy.lifecycle }}
        lifecycle:
          {{ toYaml .Values.global.proxy.lifecycle | indent 4 }}
        {{- end }}
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: INSTANCE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
        - name: HOST_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
      {{- if eq .Values.global.proxy.tracer "datadog" }}
      {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }}
      {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }}
        - name: {{ $key }}
          value: "{{ $value }}"
      {{- end }}
      {{- end }}
      {{- end }}
        - name: ISTIO_META_POD_PORTS
          value: |-
            [
            {{- $first := true }}
            {{- range $index1, $c := .Spec.Containers }}
              {{- range $index2, $p := $c.Ports }}
                {{- if (structToJSON $p) }}
                {{if not $first}},{{end}}{{ structToJSON $p }}
                {{- $first = false }}
                {{- end }}
              {{- end}}
            {{- end}}
            ]
        - name: ISTIO_META_CLUSTER_ID
          value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}"
        - name: ISTIO_META_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: ISTIO_META_CONFIG_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: SDS_ENABLED
          value: "{{ .Values.global.sds.enabled }}"
        - name: ISTIO_META_INTERCEPTION_MODE
          value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}"
        - name: ISTIO_META_INCLUDE_INBOUND_PORTS
          value: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (applicationPorts .Spec.Containers) }}"
        {{- if .Values.global.network }}
        - name: ISTIO_META_NETWORK
          value: "{{ .Values.global.network }}"
        {{- end }}
        {{ if .ObjectMeta.Annotations }}
        - name: ISTIO_METAJSON_ANNOTATIONS
          value: |
                 {{ toJSON .ObjectMeta.Annotations }}
        {{ end }}
        {{ if .ObjectMeta.Labels }}
        - name: ISTIO_METAJSON_LABELS
          value: |
                 {{ toJSON .ObjectMeta.Labels }}
        {{ end }}
        {{- if .DeploymentMeta.Name }}
        - name: ISTIO_META_WORKLOAD_NAME
          value: {{ .DeploymentMeta.Name }}
        {{ end }}
        {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }}
        - name: ISTIO_META_OWNER
          value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }}
        {{- end}}
        {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }}
        - name: ISTIO_BOOTSTRAP_OVERRIDE
          value: "/etc/istio/custom-bootstrap/custom_bootstrap.json"
        {{- end }}
        {{- if .Values.global.sds.customTokenDirectory }}
        - name: ISTIO_META_SDS_TOKEN_PATH
          value: "{{ .Values.global.sds.customTokenDirectory -}}/sdstoken"
        {{- end }}
        {{- if .Values.global.meshID }}
        - name: ISTIO_META_MESH_ID
          value: "{{ .Values.global.meshID }}"
        {{- else if .Values.global.trustDomain }}
        - name: ISTIO_META_MESH_ID
          value: "{{ .Values.global.trustDomain }}"
        {{- end }}
        {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }}
        {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }}
          - name: {{ $key }}
            value: "{{ $value }}"
        {{- end }}
        {{- end }}
        imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}"
        {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }}
        readinessProbe:
          httpGet:
            path: /healthz/ready
            port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}
          initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }}
          periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }}
          failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }}
        {{ end -}}
        securityContext:
          allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }}
          capabilities:
            {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}}
            add:
            - NET_ADMIN
            {{- end }}
            drop:
            - ALL
          privileged: {{ .Values.global.proxy.privileged }}
          readOnlyRootFilesystem: {{ not .Values.global.proxy.enableCoreDump }}
          runAsGroup: 1337
          {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}}
          runAsNonRoot: false
          runAsUser: 0
          {{- else -}}
          runAsNonRoot: true
          runAsUser: 1337
          {{- end }}
        resources:
          {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}}
          requests:
            {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}}
            cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}"
            {{ end}}
            {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}}
            memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}"
            {{ end }}
        {{ else -}}
      {{- if .Values.global.proxy.resources }}
          {{ toYaml .Values.global.proxy.resources | indent 4 }}
      {{- end }}
        {{  end -}}
        volumeMounts:
        {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }}
        - mountPath: /etc/istio/custom-bootstrap
          name: custom-bootstrap-volume
        {{- end }}
        - mountPath: /etc/istio/proxy
          name: istio-envoy
        {{- if .Values.global.sds.enabled }}
        - mountPath: /var/run/sds
          name: sds-uds-path
          readOnly: true
        - mountPath: /var/run/secrets/tokens
          name: istio-token
        {{- if .Values.global.sds.customTokenDirectory }}
        - mountPath: "{{ .Values.global.sds.customTokenDirectory -}}"
          name: custom-sds-token
          readOnly: true
        {{- end }}
        {{- else }}
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
        {{- end }}
        {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }}
        - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}
          name: lightstep-certs
          readOnly: true
        {{- end }}
          {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }}
          {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }}
        - name: "{{  $index }}"
          {{ toYaml $value | indent 4 }}
          {{ end }}
          {{- end }}
      volumes:
      {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }}
      - name: custom-bootstrap-volume
        configMap:
          name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }}
      {{- end }}
      - emptyDir:
          medium: Memory
        name: istio-envoy
      {{- if .Values.global.sds.enabled }}
      - name: sds-uds-path
        hostPath:
          path: /var/run/sds
      - name: istio-token
        projected:
          sources:
          - serviceAccountToken:
              path: istio-token
              expirationSeconds: 43200
              audience: {{ .Values.global.sds.token.aud }}
      {{- if .Values.global.sds.customTokenDirectory }}
      - name: custom-sds-token
        secret:
          secretName: sdstokensecret
      {{- end }}
      {{- else }}
      - name: istio-certs
        secret:
          optional: true
          {{ if eq .Spec.ServiceAccountName "" }}
          secretName: istio.default
          {{ else -}}
          secretName: {{  printf "istio.%s" .Spec.ServiceAccountName }}
          {{  end -}}
        {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }}
        {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }}
      - name: "{{ $index }}"
        {{ toYaml $value | indent 2 }}
        {{ end }}
        {{ end }}
      {{- end }}
      {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }}
      - name: lightstep-certs
        secret:
          optional: true
          secretName: lightstep.cacert
      {{- end }}
      {{- if .Values.global.podDNSSearchNamespaces }}
      dnsConfig:
        searches:
          {{- range .Values.global.podDNSSearchNamespaces }}
          - {{ render . }}
          {{- end }}
      {{- end }}
    injectedAnnotations:
  values: '{"certmanager":{"enabled":false,"hub":"quay.io/jetstack","image":"cert-manager-controller","namespace":"istio-system","tag":"v0.6.2"},"clusterResources":true,"cni":{"namespace":"istio-system"},"galley":{"enableAnalysis":false,"enabled":false,"image":"galley","namespace":"istio-system"},"gateways":{"istio-egressgateway":{"autoscaleEnabled":true,"enabled":false,"env":{"ISTIO_META_ROUTER_MODE":"sni-dnat"},"namespace":"istio-system","ports":[{"name":"http2","port":80},{"name":"https","port":443},{"name":"tls","port":15443,"targetPort":15443}],"secretVolumes":[{"mountPath":"/etc/istio/egressgateway-certs","name":"egressgateway-certs","secretName":"istio-egressgateway-certs"},{"mountPath":"/etc/istio/egressgateway-ca-certs","name":"egressgateway-ca-certs","secretName":"istio-egressgateway-ca-certs"}],"type":"ClusterIP","zvpn":{"enabled":true,"suffix":"global"}},"istio-ingressgateway":{"applicationPorts":"","autoscaleEnabled":true,"debug":"info","domain":"","enabled":false,"env":{"ISTIO_META_ROUTER_MODE":"sni-dnat"},"meshExpansionPorts":[{"name":"tcp-pilot-grpc-tls","port":15011,"targetPort":15011},{"name":"tcp-citadel-grpc-tls","port":8060,"targetPort":8060},{"name":"tcp-dns-tls","port":853,"targetPort":853}],"namespace":"istio-system","ports":[{"name":"status-port","port":15020,"targetPort":15020},{"name":"http2","port":80,"targetPort":80},{"name":"https","port":443},{"name":"kiali","port":15029,"targetPort":15029},{"name":"prometheus","port":15030,"targetPort":15030},{"name":"grafana","port":15031,"targetPort":15031},{"name":"tracing","port":15032,"targetPort":15032},{"name":"tls","port":15443,"targetPort":15443}],"sds":{"enabled":false,"image":"node-agent-k8s","resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}},"secretVolumes":[{"mountPath":"/etc/istio/ingressgateway-certs","name":"ingressgateway-certs","secretName":"istio-ingressgateway-certs"},{"mountPath":"/etc/istio/ingressgateway-ca-certs","name":"ingressgateway-ca-certs","secretName":"istio-ingressgateway-ca-certs"}],"type":"LoadBalancer","zvpn":{"enabled":true,"suffix":"global"}}},"global":{"arch":{"amd64":2,"ppc64le":2,"s390x":2},"certificates":[],"configNamespace":"istio-system","configValidation":true,"controlPlaneSecurityEnabled":true,"defaultNodeSelector":{},"defaultPodDisruptionBudget":{"enabled":true},"defaultResources":{"requests":{"cpu":"10m"}},"disablePolicyChecks":true,"enableHelmTest":false,"enableTracing":false,"enabled":true,"hub":"docker.io/istio","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"istioNamespace":"istio-system","k8sIngress":{"enableHttps":false,"enabled":false,"gatewayName":"ingressgateway"},"localityLbSetting":{"enabled":true},"logAsJson":true,"logging":{"level":"default:warn"},"meshExpansion":{"enabled":false,"useILB":false},"meshNetworks":{},"mtls":{"auto":false,"enabled":false},"multiCluster":{"clusterName":"","enabled":false},"namespace":"istio-system","network":"","omitSidecarInjectorConfigMap":false,"oneNamespace":false,"operatorManageWebhooks":false,"outboundTrafficPolicy":{"mode":"ALLOW_ANY"},"policyCheckFailOpen":false,"policyNamespace":"istio-system","priorityClassName":"","prometheusNamespace":"istio-system","proxy":{"accessLogEncoding":"JSON","accessLogFile":"","accessLogFormat":"","autoInject":"disabled","clusterDomain":"cluster.local","componentLogLevel":"misc:error","concurrency":2,"dnsRefreshRate":"300s","enableCoreDump":false,"envoyAccessLogService":{"enabled":false},"envoyMetricsService":{"enabled":false,"tcpKeepalive":{"interval":"10s","probes":3,"time":"10s"},"tlsSettings":{"mode":"DISABLE","subjectAltNames":[]}},"envoyStatsd":{"enabled":false},"excludeIPRanges":"","excludeInboundPorts":"","excludeOutboundPorts":"","image":"proxyv2","includeIPRanges":"10.33.0.0/16,10.32.128.0/20","includeInboundPorts":"*","kubevirtInterfaces":"","logLevel":"warning","privileged":false,"protocolDetectionTimeout":"100ms","readinessFailureThreshold":30,"readinessInitialDelaySeconds":1,"readinessPeriodSeconds":2,"resources":{"limits":{"cpu":"500m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"statusPort":15020,"tracer":"zipkin"},"proxy_init":{"image":"proxyv2","resources":{"limits":{"cpu":"100m","memory":"50Mi"},"requests":{"cpu":"10m","memory":"10Mi"}}},"sds":{"enabled":false,"token":{"aud":"istio-ca"},"udsPath":""},"securityNamespace":"istio-system","tag":"1.4.6","telemetryNamespace":"istio-system","tracer":{"datadog":{"address":"$(HOST_IP):8126"},"lightstep":{"accessToken":"","address":"","cacertPath":"","secure":true},"zipkin":{"address":""}},"trustDomain":"cluster.local","useMCP":false},"grafana":{"accessMode":"ReadWriteMany","contextPath":"/grafana","dashboardProviders":{"dashboardproviders.yaml":{"apiVersion":1,"providers":[{"disableDeletion":false,"folder":"istio","name":"istio","options":{"path":"/var/lib/grafana/dashboards/istio"},"orgId":1,"type":"file"}]}},"datasources":{"datasources.yaml":{"apiVersion":1}},"enabled":false,"env":{},"envSecrets":{},"image":{"repository":"grafana/grafana","tag":"6.4.3"},"ingress":{"enabled":false,"hosts":["grafana.local"]},"namespace":"istio-system","nodeSelector":{},"persist":false,"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"enabled":false,"passphraseKey":"passphrase","secretName":"grafana","usernameKey":"username"},"service":{"annotations":{},"externalPort":3000,"name":"http","type":"ClusterIP"},"storageClassName":"","tolerations":[]},"istio_cni":{"enabled":false,"repair":{"enabled":true}},"istiocoredns":{"coreDNSImage":"coredns/coredns","coreDNSPluginImage":"istio/coredns-plugin:0.2-istio-1.1","coreDNSTag":"1.6.2","enabled":false,"namespace":"istio-system"},"kiali":{"contextPath":"/kiali","createDemoSecret":false,"dashboard":{"passphraseKey":"passphrase","secretName":"kiali","usernameKey":"username","viewOnlyMode":false},"enabled":false,"hub":"quay.io/kiali","ingress":{"enabled":false,"hosts":["kiali.local"]},"namespace":"istio-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"cert_file":"/kiali-cert/cert-chain.pem","enabled":false,"private_key_file":"/kiali-cert/key.pem"},"tag":"v1.9"},"mixer":{"adapters":{"kubernetesenv":{"enabled":true},"prometheus":{"enabled":true,"metricsExpiryDuration":"10m"},"stackdriver":{"auth":{"apiKey":"","appCredentials":false,"serviceAccountPath":""},"enabled":false,"tracer":{"enabled":false,"sampleProbability":1}},"stdio":{"enabled":false,"outputAsJson":false},"useAdapterCRDs":false},"policy":{"adapters":{"kubernetesenv":{"enabled":true},"useAdapterCRDs":false},"autoscaleEnabled":true,"enabled":false,"image":"mixer","namespace":"istio-system","sessionAffinityEnabled":false},"telemetry":{"autoscaleEnabled":true,"enabled":false,"env":{"GOMAXPROCS":"6"},"image":"mixer","loadshedding":{"latencyThreshold":"100ms","mode":"enforce"},"namespace":"istio-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"reportBatchMaxEntries":100,"reportBatchMaxTime":"1s","sessionAffinityEnabled":false,"tolerations":[],"useMCP":true}},"nodeagent":{"enabled":false,"image":"node-agent-k8s","namespace":"istio-system"},"pilot":{"appNamespaces":[],"autoscaleEnabled":true,"autoscaleMax":5,"autoscaleMin":1,"configMap":true,"configNamespace":"istio-config","cpu":{"targetAverageUtilization":80},"enableProtocolSniffingForInbound":false,"enableProtocolSniffingForOutbound":true,"enabled":true,"env":{},"image":"pilot","ingress":{"ingressClass":"istio","ingressControllerMode":"OFF","ingressService":"istio-ingressgateway"},"keepaliveMaxServerConnectionAge":"30m","meshNetworks":{"networks":{}},"namespace":"istio-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"policy":{"enabled":false},"replicaCount":1,"tolerations":[],"traceSampling":1,"useMCP":false},"prometheus":{"contextPath":"/prometheus","enabled":false,"hub":"docker.io/prom","ingress":{"enabled":false,"hosts":["prometheus.local"]},"namespace":"istio-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"retention":"6h","scrapeInterval":"15s","security":{"enabled":true},"tag":"v2.12.0","tolerations":[]},"security":{"dnsCerts":{"istio-pilot-service-account.istio-control":"istio-pilot.istio-control"},"enableNamespacesByDefault":true,"enabled":true,"image":"citadel","namespace":"istio-system","selfSigned":true,"trustDomain":"cluster.local"},"sidecarInjectorWebhook":{"alwaysInjectSelector":[],"enableNamespacesByDefault":false,"enabled":true,"image":"sidecar_injector","injectLabel":"istio-injection","injectedAnnotations":{},"lifecycle":{},"namespace":"istio-system","neverInjectSelector":[],"nodeSelector":{},"objectSelector":{"autoInject":true,"enabled":false},"podAnnotations":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"resources":{},"rewriteAppHTTPProbe":false,"rollingMaxSurge":"100%","rollingMaxUnavailable":"25%","selfSigned":false,"tolerations":[]},"telemetry":{"enabled":true,"v1":{"enabled":true},"v2":{"enabled":false,"prometheus":{"enabled":true},"stackdriver":{"configOverride":{},"enabled":false,"logging":false,"monitoring":false,"topology":false}}},"tracing":{"enabled":false,"ingress":{"enabled":false},"jaeger":{"accessMode":"ReadWriteMany","enabled":false,"hub":"docker.io/jaegertracing","memory":{"max_traces":50000},"namespace":"istio-system","persist":false,"spanStorageType":"badger","storageClassName":"","tag":"1.14"},"nodeSelector":{},"opencensus":{"exporters":{"stackdriver":{"enable_tracing":true}},"hub":"docker.io/omnition","resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"200m","memory":"400Mi"}},"tag":"0.1.9"},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"provider":"jaeger","service":{"annotations":{},"externalPort":9411,"name":"http-query","type":"ClusterIP"},"zipkin":{"hub":"docker.io/openzipkin","javaOptsHeap":700,"maxSpans":500000,"node":{"cpus":2},"probeStartupDelay":200,"queryPort":9411,"resources":{"limits":{"cpu":"300m","memory":"900Mi"},"requests":{"cpu":"150m","memory":"900Mi"}},"tag":"2.14.2"}},"version":""}'
kind: ConfigMap
metadata:
  annotations:
  labels:
    app: sidecar-injector
    istio: sidecar-injector
    operator.istio.io/component: Injector
    operator.istio.io/managed: Reconcile
    operator.istio.io/version: 1.4.6
    release: istio
  name: cm-test-toshi
  1. Apply patch
$ kubectl patch configmap/cm-test-toshi --type merge -p '{
  "data":{
    "config":{
      "template":{
        "containers":[{
          "name":"istio-proxy",
          "lifecycle":{
            "preStop":{
              "exec":{
                "command":[\"/bin/sh\", \"-c\", \"while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0]; do sleep 1; done\"]
              }
            }
          }
        }]
      }
    }
  }
}'

Anything else we need to know?:

Environment:

Kubernetes version (use kubectl version): v1.14.10-dispatcher
Cloud provider or hardware configuration: v1.14.10-gke.17

Most helpful comment

I think I figured this out.

data.config is actually just one big string due to the multiline yaml.

apiVersion: v1
data:
  config: |-
...

If you escape your patch with that in mind it applies. Do note that it's a string patch and not acted on logically as json.

I tossed it into this tool to do that easily https://www.freeformatter.com/javascript-escape.html

kubectl patch configmap/cm-test-toshi --type merge -p '{
  "data":{
    "config": "{\r\n\t\"template\":\r\n\t{\r\n\t\t\"containers\": [\r\n\t\t{\r\n\t\t\t\"name\": \"istio-proxy\",\r\n\t\t\t\"lifecycle\":\r\n\t\t\t{\r\n\t\t\t\t\"preStop\":\r\n\t\t\t\t{\r\n\t\t\t\t\t\"exec\":\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t\"command\": [\"\/bin\/sh\", \"-c\", \"while
[ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0]; do sleep 1; done\"]\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}]\r\n\t}\r\n}"
  }
}'
configmap/cm-test-toshi patched

All 7 comments

I don't think you need to escape " as \" because the patch argument is wrapped in single quotes.

What if you try this:

$ kubectl patch configmap/cm-test-toshi --type merge -p '{
  "data":{
    "config":{
      "template":{
        "containers":[{
          "name":"istio-proxy",
          "lifecycle":{
            "preStop":{
              "exec":{
                "command":["/bin/sh", "-c", "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0]; do sleep 1; done"]
              }
            }
          }
        }]
      }
    }
  }
}'

Thank you for the advice! When I applied the patch that removed escape, the following error occurred.

Error from server: v1.ConfigMap.Data: ReadString: expects " or n, but found {, error found in #10 byte of ...|"config":{"template"|..., bigger context ...|{"apiVersion":"v1","data":{"config":{"template":{"containers":[{"lifecycle":{"preStop":|...

Does the patch still have an issue?

Different problem.

I'm not sure how you would be able to patch this configmap (it is pretty complicated). Maybe someone else can help you, or try stack overflow.

Can you provide a test case of 20-30 lines of pure yaml? The amount of templating here makes it difficult to reproduce. My gut is that this is a client problem or an issue with string escaping. Can you reproduce this on the latest version?

I think I figured this out.

data.config is actually just one big string due to the multiline yaml.

apiVersion: v1
data:
  config: |-
...

If you escape your patch with that in mind it applies. Do note that it's a string patch and not acted on logically as json.

I tossed it into this tool to do that easily https://www.freeformatter.com/javascript-escape.html

kubectl patch configmap/cm-test-toshi --type merge -p '{
  "data":{
    "config": "{\r\n\t\"template\":\r\n\t{\r\n\t\t\"containers\": [\r\n\t\t{\r\n\t\t\t\"name\": \"istio-proxy\",\r\n\t\t\t\"lifecycle\":\r\n\t\t\t{\r\n\t\t\t\t\"preStop\":\r\n\t\t\t\t{\r\n\t\t\t\t\t\"exec\":\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t\"command\": [\"\/bin\/sh\", \"-c\", \"while
[ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0]; do sleep 1; done\"]\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}]\r\n\t}\r\n}"
  }
}'
configmap/cm-test-toshi patched

Feel free to reopen this if it you think it's something else.

/close

@eddiezane: Closing this issue.

In response to this:

Feel free to reopen this if it you think it's something else.

/close

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pwittrock picture pwittrock  路  4Comments

mnussbaum picture mnussbaum  路  6Comments

drpaneas picture drpaneas  路  3Comments

cbluth picture cbluth  路  6Comments

whs-dot-hk picture whs-dot-hk  路  6Comments