When doing helm upgrade ... --force I'm getting this below error
Error: UPGRADE FAILED: failed to replace object: Service "api" is invalid: spec.clusterIP: Invalid value: "": field is immutable
And This is how my service file looks like: (Not passing clusterIP anywhere )
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
namespace: {{ .Release.Namespace }}
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
labels:
app: {{ .Chart.Name }}-service
kubernetes.io/name: {{ .Chart.Name | quote }}
dns: route53
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
spec:
selector:
app: {{ .Chart.Name }}
type: LoadBalancer
ports:
- port: 443
name: https
targetPort: http-port
protocol: TCP
Helm Version: 3.0.1
Kubectl Version: 1.13.1 [Tried with the 1.17.1 as well]
Server: 1.14
Note: Previously I was using some old version (of server, kubectl, helm) at that time I did not face this kind of issue. I can see lots of similar issues in GitHub regarding this, but unable to find any working solution for me.
few of the similar issues:
https://github.com/kubernetes/kubernetes/issues/25241
https://github.com/helm/charts/pull/13646 [For Nginx chart]
I've made some tests with Helm and got the same issue when trying to change the Service type from NodePort/ClusterIP to LoadBalancer.
This is how I've reproduced your issue:
Kubernetes 1.15.3 (GKE) Helm 3.1.1
Helm chart used for test: stable/nginx-ingress
helm fetch stable/nginx-ingress
tar xzvf nginx-ingress-1.33.0.tgz
type: LoadBalancer to type: NodePort in the values.yaml file (line 271):sed -i '271s/LoadBalancer/NodePort/' values.yaml
helm install nginx-ingress ./
NodePort:kubectl get svc -l app=nginx-ingress,component=controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.0.3.137 <none> 80:30117/TCP,443:30003/TCP 1m
LoadBalancer in the values.yaml:sed -i '271s/NodePort/LoadBalancer/' values.yaml
--force flag:helm upgrade nginx-ingress ./ --force
And then:
Error: UPGRADE FAILED: failed to replace object: Service "nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable
Digging around I found this in HELM source code:
// if --force is applied, attempt to replace the existing resource with the new object.
if force {
obj, err = helper.Replace(target.Namespace, target.Name, true, target.Object)
if err != nil {
return errors.Wrap(err, "failed to replace object")
}
c.Log("Replaced %q with kind %s for kind %s\n", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind)
} else {
// send patch to server
obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil)
if err != nil {
return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind)
}
}
Analyzing the code above Helm will use similar to kubectl replace api request (instead of kubectl replace --force as we could expect)... when the helm --force flag is set.
If not, then Helm will use kubectl patch api request to make the upgrade.
Let's check if it make sense:
NodePort:kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
labels:
app: test-svc
name: test-svc
spec:
selector:
app: test-app
ports:
- port: 80
protocol: TCP
targetPort: 80
type: NodePort
EOF
Make the service was created:
kubectl get svc -l app=test-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-svc NodePort 10.0.7.37 <none> 80:31523/TCP 25
Now lets try to use kubectl replace to upgrade the service to LoadBalancer, like helm upgrade --force:
kubectl replace -f - <<EOF
apiVersion: v1
kind: Service
metadata:
labels:
app: test-svc
name: test-svc
spec:
selector:
app: test-app
ports:
- port: 80
protocol: TCP
targetPort: 80
type: LoadBalancer
EOF
This shows the error:
The Service "test-svc" is invalid: spec.clusterIP: Invalid value: "": field is immutable
Now, lets use kubectl patch to change the NodePort to LoadBalancer, simulating the helm upgrade command without --force flag:
Here is the kubectl patch documentation, if want to see how to use.
kubectl patch svc test-svc -p '{"spec":{"type":"LoadBalancer"}}'
Then you see:
service/test-svc patched
You should to use helm upgrade without --force, it will work.
If you really need to use --force to recreate some resources, like pods to get the latest configMap update, for example, then I suggest you first manually change the service specs before Helm upgrade.
If you are trying to change the service type you could do it exporting the service yaml, changing the type and apply it again (because I experienced this behavior only when I tried to apply the same template from the first time):
kubectl get svc test-svc -o yaml | sed 's/NodePort/LoadBalancer/g' | kubectl replace --force -f -
The Output:
service "test-svc" deleted
service/test-svc replaced
Now, if you try to use helm upgrade --force and doesn't have any change to do in the service, it will work and will recreate your pods and others resources.
I hope that helps you!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With