Repository: kubernetes/kops (go)

Issue:
Failed to set env var when using kubetest2
/kind bug

**1. What `kops` version are you running? The command `kops version`, will display
 this information.**
Client version: 1.26.3 (git-v1.26.3)

**2. What Kubernetes version are you running? `kubectl version` will print the
 version if a cluster is running or provide the Kubernetes version specified as
 a `kops` flag.**
1.27

**3. What cloud provider are you using?**
aws

**4. What commands did you run?  What is the simplest way to reproduce this issue?**
```
kubetest2 kops -v=2 --cloud-provider=aws --cluster-name=scalability1.k8s.local --kops-binary-path=/Users/pgogia/workspace/golang/src/k8s.io/bin/kops --admin-access=0.0.0.0/0 --env=KOPS_FEATURE_FLAGS=ClusterAddons, --validation-wait=30m --up --kubernetes-version=v1.27.4 '--create-args=--networking=amazonvpc --set cluster.spec.networking.amazonVPC.env=ENABLE_PREFIX_DELEGATION=true --image=ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/arm64/hvm/ebs-gp2/ami-id --set spec.kubeScheduler.authorizationAlwaysAllowPaths=/healthz --set spec.kubeScheduler.authorizationAlwaysAllowPaths=/metrics --node-count=500 --node-size=c6g.medium --control-plane-count=3 --master-size=c6g.16xlarge'
```
**5. What happened after the commands executed?**
Fails with this error
```
Error: cannot set field "spec.networking.amazonVPC.env": unhandled type "kops.EnvVar"
Error: exit status 1
```
**6. What did you expect to happen?**
Cluster creation to succeed

**7. Please provide your cluster manifest. Execute
  `kops get --name my.example.com -o yaml` to display your cluster manifest.
  You may want to remove your cluster name and other sensitive information.**

```yaml
```

**8. Please run the commands with most verbose logging by adding the `-v 10` flag.
  Paste the logs into this report, or in a gist and provide the gist link here.**

**9. Anything else do we need to know?**
I am trying to set the env var for amazon vpc cni like this `--set cluster.spec.networking.amazonVPC.env=ENABLE_PREFIX_DELEGATION=true` but seems like the type `"kops.EnvVar"` is not handled [here](https://github.com/kubernetes/kops/blob/master/util/pkg/reflectutils/access.go#L197) in `setType` function.

Pull request description:
Allow setting env vars from the command line
Usage:
```
kops edit cluster --set cluster.spec.networking.amazonvpc.env=ENABLE_PREFIX_DELEGATION=true
```

Closes #15759 

Patch:
diff --git a/upup/models/cloudup/resources/addons/networking.cilium.io/k8s-1.16-v1.13.yaml.template b/upup/models/cloudup/resources/addons/networking.cilium.io/k8s-1.16-v1.13.yaml.template
index b738cdff0a..93e966be68 100644
--- a/upup/models/cloudup/resources/addons/networking.cilium.io/k8s-1.16-v1.13.yaml.template
+++ b/upup/models/cloudup/resources/addons/networking.cilium.io/k8s-1.16-v1.13.yaml.template
@@ -1237,7 +1237,7 @@ metadata:
   namespace: kube-system
 spec:
   dnsNames:
-  - "*.{{ replace .ClusterName "." "-" }}.hubble-grpc.cilium.io"
+  - "*.{{ .ClusterName }}.hubble-grpc.cilium.io"
   issuerRef:
     kind: Issuer
     name: networking.cilium.io
diff --git a/util/pkg/reflectutils/access.go b/util/pkg/reflectutils/access.go
index 482ff2df3b..09238fc540 100644
--- a/util/pkg/reflectutils/access.go
+++ b/util/pkg/reflectutils/access.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/kops/pkg/apis/kops"
 )
 
 func SetString(target interface{}, targetPath string, newValue string) error {
@@ -188,6 +189,16 @@ func setType(v reflect.Value, newValue string) error {
 	case "intstr.IntOrString":
 		newV = reflect.ValueOf(intstr.Parse(newValue))
 
+	case "kops.EnvVar":
+		name, value, found := strings.Cut(newValue, "=")
+		envVar := kops.EnvVar{
+			Name: name,
+		}
+		if found {
+			envVar.Value = value
+		}
+		newV = reflect.ValueOf(envVar)
+
 	default:
 		// This handles enums and other simple conversions
 		newV = reflect.ValueOf(newValue)


Test patch:
diff --git a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_cluster-completed.spec_content b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_cluster-completed.spec_content
index 5a313ad1dd..81daf221c9 100644
--- a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_cluster-completed.spec_content
+++ b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_cluster-completed.spec_content
@@ -207,7 +207,7 @@ spec:
       bpfNeighGlobalMax: 524288
       bpfPolicyMapMax: 16384
       clusterID: 253
-      clusterName: privatecilium.example.com
+      clusterName: default
       cpuRequest: 25m
       disableCNPStatusUpdates: true
       disableMasquerade: false
diff --git a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-bootstrap_content b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-bootstrap_content
index 1923c60e12..c20f3fcb42 100644
--- a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-bootstrap_content
+++ b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-bootstrap_content
@@ -162,7 +162,7 @@ spec:
     version: 9.99.0
   - id: k8s-1.16
     manifest: networking.cilium.io/k8s-1.16-v1.13.yaml
-    manifestHash: 41a6598e0f382e210ad03cb13b8f247161399691248fb677f43f78ad25487a3a
+    manifestHash: d2ad53421a11b1a5fca6315c724813682a49d4c2e66b0213d1e6faf6efb1cb50
     name: networking.cilium.io
     needsPKI: true
     needsRollingUpdate: all
diff --git a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-networking.cilium.io-k8s-1.16_content b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-networking.cilium.io-k8s-1.16_content
index 966a97901b..823ad8dd7a 100644
--- a/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-networking.cilium.io-k8s-1.16_content
+++ b/tests/integration/update_cluster/privatecilium2/data/aws_s3_object_privatecilium.example.com-addons-networking.cilium.io-k8s-1.16_content
@@ -52,7 +52,7 @@ data:
   bpf-policy-map-max: "16384"
   cgroup-root: /run/cilium/cgroupv2
   cluster-id: "253"
-  cluster-name: privatecilium.example.com
+  cluster-name: default
   debug: "false"
   disable-cnp-status-updates: "true"
   disable-endpoint-crd: "false"
@@ -945,7 +945,7 @@ metadata:
   namespace: kube-system
 spec:
   dnsNames:
-  - '*.privatecilium-example-com.hubble-grpc.cilium.io'
+  - '*.default.hubble-grpc.cilium.io'
   issuerRef:
     kind: Issuer
     name: networking.cilium.io
diff --git a/tests/integration/update_cluster/privatecilium2/in-v1alpha2.yaml b/tests/integration/update_cluster/privatecilium2/in-v1alpha2.yaml
index 1646cfc767..9cf91f3a0c 100644
--- a/tests/integration/update_cluster/privatecilium2/in-v1alpha2.yaml
+++ b/tests/integration/update_cluster/privatecilium2/in-v1alpha2.yaml
@@ -28,7 +28,6 @@ spec:
   networkCIDR: 172.20.0.0/16
   networking:
     cilium:
-      clusterName: privatecilium.example.com
       clusterID: 253
       hubble:
         enabled: true
diff --git a/util/pkg/reflectutils/access_test.go b/util/pkg/reflectutils/access_test.go
index a3fccc0bfe..82d9d04b8c 100644
--- a/util/pkg/reflectutils/access_test.go
+++ b/util/pkg/reflectutils/access_test.go
@@ -22,6 +22,8 @@ import (
 	"reflect"
 	"strings"
 	"testing"
+
+	"k8s.io/kops/pkg/apis/kops"
 )
 
 type fakeEnum string
@@ -56,6 +58,8 @@ type fakeObjectContainers struct {
 	Int32 *int32 `json:"int32"`
 	Int64 *int64 `json:"int64"`
 
+	Env []kops.EnvVar `json:"env"`
+
 	Enum      fakeEnum   `json:"enum"`
 	EnumSlice []fakeEnum `json:"enumSlice"`
 }
@@ -169,6 +173,20 @@ func TestSet(t *testing.T) {
 			Path:     "spec.containers[0].enumSlice",
 			Value:    "GHI,JKL",
 		},
+		{
+			Name:     "set env var",
+			Input:    "{ 'spec': { 'containers': [ {} ] } }",
+			Expected: "{ 'spec': { 'containers': [ { 'env': [ { 'name': 'ABC' } ] } ] } }",
+			Path:     "spec.containers[0].env",
+			Value:    "ABC",
+		},
+		{
+			Name:     "set env var with val",
+			Input:    "{ 'spec': { 'containers': [ {} ] } }",
+			Expected: "{ 'spec': { 'containers': [ { 'env': [ { 'name': 'ABC', 'value': 'DEF' } ] } ] } }",
+			Path:     "spec.containers[0].env",
+			Value:    "ABC=DEF",
+		},
 		// Not sure if we should do this...
 		// {
 		// 	Name:     "creating missing array elements",