diff --git a/cmd/k8s-operator/deploy/chart/values.yaml b/cmd/k8s-operator/deploy/chart/values.yaml index 9862ed5f70940..c0d33acc9f3af 100644 --- a/cmd/k8s-operator/deploy/chart/values.yaml +++ b/cmd/k8s-operator/deploy/chart/values.yaml @@ -23,6 +23,7 @@ operatorConfig: - "tag:k8s-operator" image: + # Repository defaults to DockerHub, but images are also synced to ghcr.io/tailscale/k8s-operator. repository: tailscale/k8s-operator # Digest will be prioritized over tag. If neither are set appVersion will be # used. @@ -57,6 +58,7 @@ operatorConfig: # https://tailscale.com/kb/1236/kubernetes-operator#cluster-resource-customization-using-proxyclass-custom-resource proxyConfig: image: + # Repository defaults to DockerHub, but images are also synced to ghcr.io/tailscale/tailscale. repository: tailscale/tailscale # Digest will be prioritized over tag. If neither are set appVersion will be # used. diff --git a/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml b/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml index 9d1d587a1523c..7ab7a31ade42f 100644 --- a/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml +++ b/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml @@ -721,6 +721,16 @@ spec: value: description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' type: string + image: + description: Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + enum: + - Always + - Never + - IfNotPresent resources: description: Container resource requirements. By default Tailscale Kubernetes operator does not apply any resource requirements. The amount of resources required wil depend on the amount of resources the operator needs to parse, usage patterns and cluster size. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources type: object @@ -864,6 +874,16 @@ spec: value: description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' type: string + image: + description: Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + enum: + - Always + - Never + - IfNotPresent resources: description: Container resource requirements. By default Tailscale Kubernetes operator does not apply any resource requirements. The amount of resources required wil depend on the amount of resources the operator needs to parse, usage patterns and cluster size. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources type: object diff --git a/cmd/k8s-operator/deploy/examples/proxyclass.yaml b/cmd/k8s-operator/deploy/examples/proxyclass.yaml index 3f0d2afa5eeaf..ec4bfb43b3daa 100644 --- a/cmd/k8s-operator/deploy/examples/proxyclass.yaml +++ b/cmd/k8s-operator/deploy/examples/proxyclass.yaml @@ -15,3 +15,9 @@ spec: kubernetes.io/os: "linux" imagePullSecrets: - name: "foo" + tailscaleContainer: + image: "ghcr.io/tailscale/tailscale:v1.64.0" + imagePullPolicy: IfNotPresent + tailscaleInitContainer: + image: "ghcr.io/tailscale/tailscale:v1.64.0" + imagePullPolicy: IfNotPresent diff --git a/cmd/k8s-operator/deploy/manifests/operator.yaml b/cmd/k8s-operator/deploy/manifests/operator.yaml index 78553542f909b..bd05e7709623b 100644 --- a/cmd/k8s-operator/deploy/manifests/operator.yaml +++ b/cmd/k8s-operator/deploy/manifests/operator.yaml @@ -970,6 +970,16 @@ spec: - name type: object type: array + image: + description: Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + enum: + - Always + - Never + - IfNotPresent + type: string resources: description: Container resource requirements. By default Tailscale Kubernetes operator does not apply any resource requirements. The amount of resources required wil depend on the amount of resources the operator needs to parse, usage patterns and cluster size. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources properties: @@ -1113,6 +1123,16 @@ spec: - name type: object type: array + image: + description: Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + enum: + - Always + - Never + - IfNotPresent + type: string resources: description: Container resource requirements. By default Tailscale Kubernetes operator does not apply any resource requirements. The amount of resources required wil depend on the amount of resources the operator needs to parse, usage patterns and cluster size. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources properties: diff --git a/cmd/k8s-operator/proxyclass.go b/cmd/k8s-operator/proxyclass.go index a76a67a8985dc..328e0fcb368c5 100644 --- a/cmd/k8s-operator/proxyclass.go +++ b/cmd/k8s-operator/proxyclass.go @@ -10,6 +10,7 @@ import ( "fmt" "strings" + dockerref "github.com/distribution/reference" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" @@ -111,6 +112,21 @@ func (a *ProxyClassReconciler) validate(pc *tsapi.ProxyClass) (violations field. a.recorder.Event(pc, corev1.EventTypeWarning, reasonCustomTSEnvVar, fmt.Sprintf(messageCustomTSEnvVar, string(e.Name), "tailscale")) } } + if tc.Image != "" { + // Same validation as used by kubelet https://github.com/kubernetes/kubernetes/blob/release-1.30/pkg/kubelet/images/image_manager.go#L212 + if _, err := dockerref.ParseNormalizedNamed(tc.Image); err != nil { + violations = append(violations, field.TypeInvalid(field.NewPath("spec", "statefulSet", "pod", "tailscaleContainer", "image"), tc.Image, err.Error())) + + } + } + } + if tc := pod.TailscaleContainer; tc != nil { + if tc.Image != "" { + // Same validation as used by kubelet https://github.com/kubernetes/kubernetes/blob/release-1.30/pkg/kubelet/images/image_manager.go#L212 + if _, err := dockerref.ParseNormalizedNamed(tc.Image); err != nil { + violations = append(violations, field.TypeInvalid(field.NewPath("spec", "statefulset", "pod", "tailscaleInitContainer", "image"), tc.Image, err.Error())) + } + } } } } diff --git a/cmd/k8s-operator/proxyclass_test.go b/cmd/k8s-operator/proxyclass_test.go index 4cc4f7844ff35..28a2071464abd 100644 --- a/cmd/k8s-operator/proxyclass_test.go +++ b/cmd/k8s-operator/proxyclass_test.go @@ -36,9 +36,13 @@ func TestProxyClass(t *testing.T) { Labels: map[string]string{"foo": "bar", "xyz1234": "abc567"}, Annotations: map[string]string{"foo.io/bar": "{'key': 'val1232'}"}, Pod: &tsapi.Pod{ - Labels: map[string]string{"foo": "bar", "xyz1234": "abc567"}, - Annotations: map[string]string{"foo.io/bar": "{'key': 'val1232'}"}, - TailscaleContainer: &tsapi.Container{Env: []tsapi.Env{{Name: "FOO", Value: "BAR"}}}, + Labels: map[string]string{"foo": "bar", "xyz1234": "abc567"}, + Annotations: map[string]string{"foo.io/bar": "{'key': 'val1232'}"}, + TailscaleContainer: &tsapi.Container{ + Env: []tsapi.Env{{Name: "FOO", Value: "BAR"}}, + ImagePullPolicy: "IfNotPresent", + Image: "ghcr.my-repo/tailscale:v0.01testsomething", + }, }, }, }, @@ -73,7 +77,7 @@ func TestProxyClass(t *testing.T) { expectEqual(t, fc, pc, nil) - // 2. An invalid ProxyClass resource gets its status updated to Invalid. + // 2. A ProxyClass resource with invalid labels gets its status updated to Invalid with an error message. pc.Spec.StatefulSet.Labels["foo"] = "?!someVal" mustUpdate(t, fc, "", "test", func(proxyClass *tsapi.ProxyClass) { proxyClass.Spec.StatefulSet.Labels = pc.Spec.StatefulSet.Labels @@ -85,9 +89,23 @@ func TestProxyClass(t *testing.T) { expectedEvent := "Warning ProxyClassInvalid ProxyClass is not valid: .spec.statefulSet.labels: Invalid value: \"?!someVal\": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')" expectEvents(t, fr, []string{expectedEvent}) - // 2. An valid ProxyClass but with a Tailscale env vars set results in warning events. + // 3. A ProxyClass resource with invalid image reference gets it status updated to Invalid with an error message. + pc.Spec.StatefulSet.Labels = nil + pc.Spec.StatefulSet.Pod.TailscaleContainer.Image = "FOO bar" mustUpdate(t, fc, "", "test", func(proxyClass *tsapi.ProxyClass) { - proxyClass.Spec.StatefulSet.Labels = nil // unset invalid labels from the previous test + proxyClass.Spec.StatefulSet.Labels = nil + proxyClass.Spec.StatefulSet.Pod.TailscaleContainer.Image = pc.Spec.StatefulSet.Pod.TailscaleContainer.Image + }) + expectReconciled(t, pcr, "", "test") + msg = `ProxyClass is not valid: [spec.statefulSet.pod.tailscaleContainer.image: Invalid value: "FOO bar": invalid reference format: repository name (library/FOO bar) must be lowercase, spec.statefulset.pod.tailscaleInitContainer.image: Invalid value: "FOO bar": invalid reference format: repository name (library/FOO bar) must be lowercase]` + tsoperator.SetProxyClassCondition(pc, tsapi.ProxyClassready, metav1.ConditionFalse, reasonProxyClassInvalid, msg, 0, cl, zl.Sugar()) + expectEqual(t, fc, pc, nil) + expectedEvent = `Warning ProxyClassInvalid ProxyClass is not valid: [spec.statefulSet.pod.tailscaleContainer.image: Invalid value: "FOO bar": invalid reference format: repository name (library/FOO bar) must be lowercase, spec.statefulset.pod.tailscaleInitContainer.image: Invalid value: "FOO bar": invalid reference format: repository name (library/FOO bar) must be lowercase]` + expectEvents(t, fr, []string{expectedEvent}) + + // 4. An valid ProxyClass but with a Tailscale env vars set results in warning events. + mustUpdate(t, fc, "", "test", func(proxyClass *tsapi.ProxyClass) { + proxyClass.Spec.StatefulSet.Pod.TailscaleContainer.Image = "" // unset invalid image from the previous test proxyClass.Spec.StatefulSet.Pod.TailscaleContainer.Env = []tsapi.Env{{Name: "TS_USERSPACE", Value: "true"}, {Name: "EXPERIMENTAL_TS_CONFIGFILE_PATH"}, {Name: "EXPERIMENTAL_ALLOW_PROXYING_CLUSTER_TRAFFIC_VIA_INGRESS"}} }) expectedEvents := []string{"Warning CustomTSEnvVar ProxyClass overrides the default value for TS_USERSPACE env var for tailscale container. Running with custom values for Tailscale env vars is not recommended and might break in the future.", diff --git a/cmd/k8s-operator/sts.go b/cmd/k8s-operator/sts.go index d2527da6fab73..0104df4ea67ce 100644 --- a/cmd/k8s-operator/sts.go +++ b/cmd/k8s-operator/sts.go @@ -690,6 +690,12 @@ func applyProxyClassToStatefulSet(pc *tsapi.ProxyClass, ss *appsv1.StatefulSet, // in the env var list overrides an earlier one. base.Env = append(base.Env, corev1.EnvVar{Name: string(e.Name), Value: e.Value}) } + if overlay.Image != "" { + base.Image = overlay.Image + } + if overlay.ImagePullPolicy != "" { + base.ImagePullPolicy = overlay.ImagePullPolicy + } return base } for i, c := range ss.Spec.Template.Spec.Containers { diff --git a/cmd/k8s-operator/sts_test.go b/cmd/k8s-operator/sts_test.go index cca0167ce0839..b2b2c8b93a2d7 100644 --- a/cmd/k8s-operator/sts_test.go +++ b/cmd/k8s-operator/sts_test.go @@ -81,7 +81,9 @@ func Test_applyProxyClassToStatefulSet(t *testing.T) { Limits: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1000m"), corev1.ResourceMemory: resource.MustParse("128Mi")}, Requests: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("500m"), corev1.ResourceMemory: resource.MustParse("64Mi")}, }, - Env: []tsapi.Env{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}, + Env: []tsapi.Env{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}, + ImagePullPolicy: "IfNotPresent", + Image: "ghcr.io/my-repo/tailscale:v0.01testsomething", }, TailscaleInitContainer: &tsapi.Container{ SecurityContext: &corev1.SecurityContext{ @@ -92,7 +94,9 @@ func Test_applyProxyClassToStatefulSet(t *testing.T) { Limits: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1000m"), corev1.ResourceMemory: resource.MustParse("128Mi")}, Requests: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("500m"), corev1.ResourceMemory: resource.MustParse("64Mi")}, }, - Env: []tsapi.Env{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}, + Env: []tsapi.Env{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}, + ImagePullPolicy: "IfNotPresent", + Image: "ghcr.io/my-repo/tailscale:v0.01testsomething", }, }, }, @@ -135,10 +139,12 @@ func Test_applyProxyClassToStatefulSet(t *testing.T) { env := []corev1.EnvVar{{Name: "TS_HOSTNAME", Value: "nginx"}} userspaceProxySS.Labels = labels userspaceProxySS.Annotations = annots + userspaceProxySS.Spec.Template.Spec.Containers[0].Image = "tailscale/tailscale:v0.0.1" userspaceProxySS.Spec.Template.Spec.Containers[0].Env = env nonUserspaceProxySS.ObjectMeta.Labels = labels nonUserspaceProxySS.ObjectMeta.Annotations = annots nonUserspaceProxySS.Spec.Template.Spec.Containers[0].Env = env + nonUserspaceProxySS.Spec.Template.Spec.InitContainers[0].Image = "tailscale/tailscale:v0.0.1" // 1. Test that a ProxyClass with all fields set gets correctly applied // to a Statefulset built from non-userspace proxy template. @@ -159,6 +165,10 @@ func Test_applyProxyClassToStatefulSet(t *testing.T) { wantSS.Spec.Template.Spec.InitContainers[0].Resources = proxyClassAllOpts.Spec.StatefulSet.Pod.TailscaleInitContainer.Resources wantSS.Spec.Template.Spec.InitContainers[0].Env = append(wantSS.Spec.Template.Spec.InitContainers[0].Env, []corev1.EnvVar{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}...) wantSS.Spec.Template.Spec.Containers[0].Env = append(wantSS.Spec.Template.Spec.Containers[0].Env, []corev1.EnvVar{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}...) + wantSS.Spec.Template.Spec.Containers[0].Image = "ghcr.io/my-repo/tailscale:v0.01testsomething" + wantSS.Spec.Template.Spec.Containers[0].ImagePullPolicy = "IfNotPresent" + wantSS.Spec.Template.Spec.InitContainers[0].Image = "ghcr.io/my-repo/tailscale:v0.01testsomething" + wantSS.Spec.Template.Spec.InitContainers[0].ImagePullPolicy = "IfNotPresent" gotSS := applyProxyClassToStatefulSet(proxyClassAllOpts, nonUserspaceProxySS.DeepCopy(), new(tailscaleSTSConfig), zl.Sugar()) if diff := cmp.Diff(gotSS, wantSS); diff != "" { @@ -194,9 +204,11 @@ func Test_applyProxyClassToStatefulSet(t *testing.T) { wantSS.Spec.Template.Spec.Containers[0].SecurityContext = proxyClassAllOpts.Spec.StatefulSet.Pod.TailscaleContainer.SecurityContext wantSS.Spec.Template.Spec.Containers[0].Resources = proxyClassAllOpts.Spec.StatefulSet.Pod.TailscaleContainer.Resources wantSS.Spec.Template.Spec.Containers[0].Env = append(wantSS.Spec.Template.Spec.Containers[0].Env, []corev1.EnvVar{{Name: "foo", Value: "bar"}, {Name: "TS_USERSPACE", Value: "true"}, {Name: "bar"}}...) + wantSS.Spec.Template.Spec.Containers[0].ImagePullPolicy = "IfNotPresent" + wantSS.Spec.Template.Spec.Containers[0].Image = "ghcr.io/my-repo/tailscale:v0.01testsomething" gotSS = applyProxyClassToStatefulSet(proxyClassAllOpts, userspaceProxySS.DeepCopy(), new(tailscaleSTSConfig), zl.Sugar()) if diff := cmp.Diff(gotSS, wantSS); diff != "" { - t.Fatalf("Unexpected result applying ProxyClass with custom labels and annotations to a StatefulSet for a userspace proxy (-got +want):\n%s", diff) + t.Fatalf("Unexpected result applying ProxyClass with all options to a StatefulSet for a userspace proxy (-got +want):\n%s", diff) } // 4. Test that a ProxyClass with custom labels and annotations gets correctly applied diff --git a/go.mod b/go.mod index a06906388a91b..8b88c7609e442 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/dave/patsy v0.0.0-20210517141501-957256f50cba github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e + github.com/distribution/reference v0.6.0 github.com/djherbis/times v1.6.0 github.com/dsnet/try v0.0.3 github.com/evanw/esbuild v0.19.11 diff --git a/go.sum b/go.sum index 66f8f5b2b1616..9ab32f0289e09 100644 --- a/go.sum +++ b/go.sum @@ -251,6 +251,8 @@ github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20 github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/docker/cli v25.0.0+incompatible h1:zaimaQdnX7fYWFqzN88exE9LDEvRslexpFowZBX6GoQ= diff --git a/k8s-operator/api.md b/k8s-operator/api.md index 4b1d59178eab5..0915b6ae6c607 100644 --- a/k8s-operator/api.md +++ b/k8s-operator/api.md @@ -2446,6 +2446,22 @@ Configuration for the proxy container running tailscale. List of environment variables to set in the container. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables Note that environment variables provided here will take precedence over Tailscale-specific environment variables set by the operator, however running proxies with custom values for Tailscale environment variables (i.e TS_USERSPACE) is not recommended and might break in the future.
false + + image + string + + Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image
+ + false + + imagePullPolicy + enum + + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image
+
+ Enum: Always, Never, IfNotPresent
+ + false resources object @@ -2857,6 +2873,22 @@ Configuration for the proxy init container that enables forwarding. List of environment variables to set in the container. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables Note that environment variables provided here will take precedence over Tailscale-specific environment variables set by the operator, however running proxies with custom values for Tailscale environment variables (i.e TS_USERSPACE) is not recommended and might break in the future.
false + + image + string + + Container image name. By default images are pulled from docker.io/tailscale/tailscale, but the official images are also available at ghcr.io/tailscale/tailscale. Image name provided here will override any proxy image values specified via the Kubernetes operator's Helm chart values or PROXY_IMAGE env var to the operator deployment. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image
+ + false + + imagePullPolicy + enum + + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image
+
+ Enum: Always, Never, IfNotPresent
+ + false resources object diff --git a/k8s-operator/apis/v1alpha1/types_proxyclass.go b/k8s-operator/apis/v1alpha1/types_proxyclass.go index 82db0f69eae72..2d80f46dec735 100644 --- a/k8s-operator/apis/v1alpha1/types_proxyclass.go +++ b/k8s-operator/apis/v1alpha1/types_proxyclass.go @@ -151,15 +151,29 @@ type Metrics struct { } type Container struct { - // Container security context. - // Security context specified here will override the security context by the operator. - // By default the operator: - // - sets 'privileged: true' for the init container - // - set NET_ADMIN capability for tailscale container for proxies that - // are created for Services or Connector. - // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context + // List of environment variables to set in the container. + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables + // Note that environment variables provided here will take precedence + // over Tailscale-specific environment variables set by the operator, + // however running proxies with custom values for Tailscale environment + // variables (i.e TS_USERSPACE) is not recommended and might break in + // the future. // +optional - SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + Env []Env `json:"env,omitempty"` + // Container image name. By default images are pulled from + // docker.io/tailscale/tailscale, but the official images are also + // available at ghcr.io/tailscale/tailscale. Image name provided here + // will override any proxy image values specified via the Kubernetes + // operator's Helm chart values or PROXY_IMAGE env var to the operator + // deployment. + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + // +optional + Image string `json:"image,omitempty"` + // Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + // +kubebuilder:validation:Enum=Always;Never;IfNotPresent + // +optional + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` // Container resource requirements. // By default Tailscale Kubernetes operator does not apply any resource // requirements. The amount of resources required wil depend on the @@ -168,15 +182,15 @@ type Container struct { // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources // +optional Resources corev1.ResourceRequirements `json:"resources,omitempty"` - // List of environment variables to set in the container. - // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables - // Note that environment variables provided here will take precedence - // over Tailscale-specific environment variables set by the operator, - // however running proxies with custom values for Tailscale environment - // variables (i.e TS_USERSPACE) is not recommended and might break in - // the future. + // Container security context. + // Security context specified here will override the security context by the operator. + // By default the operator: + // - sets 'privileged: true' for the init container + // - set NET_ADMIN capability for tailscale container for proxies that + // are created for Services or Connector. + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context // +optional - Env []Env `json:"env,omitempty"` + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` } type Env struct { diff --git a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go index 3d5840ad2e254..8c88b1aab736d 100644 --- a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go +++ b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go @@ -140,17 +140,17 @@ func (in *ConnectorStatus) DeepCopy() *ConnectorStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Container) DeepCopyInto(out *Container) { *out = *in - if in.SecurityContext != nil { - in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) - } - in.Resources.DeepCopyInto(&out.Resources) if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]Env, len(*in)) copy(*out, *in) } + in.Resources.DeepCopyInto(&out.Resources) + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container.