mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-08 23:49:56 +00:00
cmd/k8s-operator,k8s-operator,kube: Add TSRecorder CRD + controller (#13299)
cmd/k8s-operator,k8s-operator,kube: Add TSRecorder CRD + controller Deploys tsrecorder images to the operator's cluster. S3 storage is configured via environment variables from a k8s Secret. Currently only supports a single tsrecorder replica, but I've tried to take early steps towards supporting multiple replicas by e.g. having a separate secret for auth and state storage. Example CR: ```yaml apiVersion: tailscale.com/v1alpha1 kind: Recorder metadata: name: rec spec: enableUI: true ``` Updates #13298 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
- [DNSConfigList](#dnsconfiglist)
|
||||
- [ProxyClass](#proxyclass)
|
||||
- [ProxyClassList](#proxyclasslist)
|
||||
- [Recorder](#recorder)
|
||||
- [RecorderList](#recorderlist)
|
||||
|
||||
|
||||
|
||||
@@ -236,6 +238,7 @@ _Appears in:_
|
||||
|
||||
_Appears in:_
|
||||
- [Container](#container)
|
||||
- [RecorderContainer](#recordercontainer)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -258,23 +261,6 @@ _Appears in:_
|
||||
|
||||
|
||||
|
||||
#### Image
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [Nameserver](#nameserver)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `repo` _string_ | Repo defaults to tailscale/k8s-nameserver. | | |
|
||||
| `tag` _string_ | Tag defaults to operator's own tag. | | |
|
||||
|
||||
|
||||
#### Metrics
|
||||
|
||||
|
||||
@@ -319,7 +305,24 @@ _Appears in:_
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `image` _[Image](#image)_ | Nameserver image. | | |
|
||||
| `image` _[NameserverImage](#nameserverimage)_ | Nameserver image. Defaults to tailscale/k8s-nameserver:unstable. | | |
|
||||
|
||||
|
||||
#### NameserverImage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [Nameserver](#nameserver)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `repo` _string_ | Repo defaults to tailscale/k8s-nameserver. | | |
|
||||
| `tag` _string_ | Tag defaults to unstable. | | |
|
||||
|
||||
|
||||
#### NameserverStatus
|
||||
@@ -447,6 +450,145 @@ _Appears in:_
|
||||
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#condition-v1-meta) array_ | List of status conditions to indicate the status of the ProxyClass.<br />Known condition types are `ProxyClassReady`. | | |
|
||||
|
||||
|
||||
#### Recorder
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderList](#recorderlist)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `apiVersion` _string_ | `tailscale.com/v1alpha1` | | |
|
||||
| `kind` _string_ | `Recorder` | | |
|
||||
| `kind` _string_ | Kind is a string value representing the REST resource this object represents.<br />Servers may infer this from the endpoint the client submits requests to.<br />Cannot be updated.<br />In CamelCase.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | |
|
||||
| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.<br />Servers should convert recognized schemas to the latest internal value, and<br />may reject unrecognized values.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | |
|
||||
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
|
||||
| `spec` _[RecorderSpec](#recorderspec)_ | Spec describes the desired recorder instance. | | |
|
||||
| `status` _[RecorderStatus](#recorderstatus)_ | RecorderStatus describes the status of the recorder. This is set<br />and managed by the Tailscale operator. | | |
|
||||
|
||||
|
||||
#### RecorderContainer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderPod](#recorderpod)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `env` _[Env](#env) array_ | List of environment variables to set in the container.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables<br />Note that environment variables provided here will take precedence<br />over Tailscale-specific environment variables set by the operator,<br />however running proxies with custom values for Tailscale environment<br />variables (i.e TS_USERSPACE) is not recommended and might break in<br />the future. | | |
|
||||
| `image` _string_ | Container image name including tag. Defaults to docker.io/tailscale/tsrecorder<br />with the same tag as the operator, but the official images are also<br />available at ghcr.io/tailscale/tsrecorder.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image | | |
|
||||
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#pullpolicy-v1-core)_ | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image | | Enum: [Always Never IfNotPresent] <br /> |
|
||||
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#resourcerequirements-v1-core)_ | Container resource requirements.<br />By default, the operator does not apply any resource requirements. The<br />amount of resources required wil depend on the volume of recordings sent.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources | | |
|
||||
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#securitycontext-v1-core)_ | Container security context. By default, the operator does not apply any<br />container security context.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context | | |
|
||||
|
||||
|
||||
#### RecorderList
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `apiVersion` _string_ | `tailscale.com/v1alpha1` | | |
|
||||
| `kind` _string_ | `RecorderList` | | |
|
||||
| `kind` _string_ | Kind is a string value representing the REST resource this object represents.<br />Servers may infer this from the endpoint the client submits requests to.<br />Cannot be updated.<br />In CamelCase.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | |
|
||||
| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.<br />Servers should convert recognized schemas to the latest internal value, and<br />may reject unrecognized values.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | |
|
||||
| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
|
||||
| `items` _[Recorder](#recorder) array_ | | | |
|
||||
|
||||
|
||||
#### RecorderPod
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderStatefulSet](#recorderstatefulset)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `labels` _object (keys:string, values:string)_ | Labels that will be added to Recorder Pods. Any labels specified here<br />will be merged with the default labels applied to the Pod by the operator.<br />https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set | | |
|
||||
| `annotations` _object (keys:string, values:string)_ | Annotations that will be added to Recorder Pods. Any annotations<br />specified here will be merged with the default annotations applied to<br />the Pod by the operator.<br />https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set | | |
|
||||
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#affinity-v1-core)_ | Affinity rules for Recorder Pods. By default, the operator does not<br />apply any affinity rules.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#affinity | | |
|
||||
| `container` _[RecorderContainer](#recordercontainer)_ | Configuration for the Recorder container running tailscale. | | |
|
||||
| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#podsecuritycontext-v1-core)_ | Security context for Recorder Pods. By default, the operator does not<br />apply any Pod security context.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-2 | | |
|
||||
| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#localobjectreference-v1-core) array_ | Image pull Secrets for Recorder Pods.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec | | |
|
||||
| `nodeSelector` _object (keys:string, values:string)_ | Node selector rules for Recorder Pods. By default, the operator does<br />not apply any node selector rules.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | |
|
||||
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#toleration-v1-core) array_ | Tolerations for Recorder Pods. By default, the operator does not apply<br />any tolerations.<br />https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling | | |
|
||||
|
||||
|
||||
#### RecorderSpec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [Recorder](#recorder)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `statefulSet` _[RecorderStatefulSet](#recorderstatefulset)_ | Configuration parameters for the Recorder's StatefulSet. The operator<br />deploys a StatefulSet for each Recorder resource. | | |
|
||||
| `tags` _[Tags](#tags)_ | Tags that the Tailscale device will be tagged with. Defaults to [tag:k8s].<br />If you specify custom tags here, make sure you also make the operator<br />an owner of these tags.<br />See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator.<br />Tags cannot be changed once a Recorder node has been created.<br />Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$. | | Pattern: `^tag:[a-zA-Z][a-zA-Z0-9-]*$` <br />Type: string <br /> |
|
||||
| `enableUI` _boolean_ | Set to true to enable the Recorder UI. The UI lists and plays recorded sessions.<br />The UI will be served at <MagicDNS name of the recorder>:443. Defaults to false.<br />Corresponds to --ui tsrecorder flag https://tailscale.com/kb/1246/tailscale-ssh-session-recording#deploy-a-recorder-node.<br />Required if S3 storage is not set up, to ensure that recordings are accessible. | | |
|
||||
| `storage` _[Storage](#storage)_ | Configure where to store session recordings. By default, recordings will<br />be stored in a local ephemeral volume, and will not be persisted past the<br />lifetime of a specific pod. | | |
|
||||
|
||||
|
||||
#### RecorderStatefulSet
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderSpec](#recorderspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `labels` _object (keys:string, values:string)_ | Labels that will be added to the StatefulSet created for the Recorder.<br />Any labels specified here will be merged with the default labels applied<br />to the StatefulSet by the operator.<br />https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set | | |
|
||||
| `annotations` _object (keys:string, values:string)_ | Annotations that will be added to the StatefulSet created for the Recorder.<br />Any Annotations specified here will be merged with the default annotations<br />applied to the StatefulSet by the operator.<br />https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set | | |
|
||||
| `pod` _[RecorderPod](#recorderpod)_ | Configuration for pods created by the Recorder's StatefulSet. | | |
|
||||
|
||||
|
||||
#### RecorderStatus
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [Recorder](#recorder)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#condition-v1-meta) array_ | List of status conditions to indicate the status of the Recorder.<br />Known condition types are `RecorderReady`. | | |
|
||||
| `devices` _[TailnetDevice](#tailnetdevice) array_ | List of tailnet devices associated with the Recorder statefulset. | | |
|
||||
|
||||
|
||||
#### Route
|
||||
|
||||
_Underlying type:_ _string_
|
||||
@@ -478,6 +620,56 @@ _Appears in:_
|
||||
|
||||
|
||||
|
||||
#### S3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [Storage](#storage)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `endpoint` _string_ | S3-compatible endpoint, e.g. s3.us-east-1.amazonaws.com. | | |
|
||||
| `bucket` _string_ | Bucket name to write to. The bucket is expected to be used solely for<br />recordings, as there is no stable prefix for written object names. | | |
|
||||
| `credentials` _[S3Credentials](#s3credentials)_ | Configure environment variable credentials for managing objects in the<br />configured bucket. If not set, tsrecorder will try to acquire credentials<br />first from the file system and then the STS API. | | |
|
||||
|
||||
|
||||
#### S3Credentials
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [S3](#s3)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secret` _[S3Secret](#s3secret)_ | Use a Kubernetes Secret from the operator's namespace as the source of<br />credentials. | | |
|
||||
|
||||
|
||||
#### S3Secret
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [S3Credentials](#s3credentials)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `name` _string_ | The name of a Kubernetes Secret in the operator's namespace that contains<br />credentials for writing to the configured bucket. Each key-value pair<br />from the secret's data will be mounted as an environment variable. It<br />should include keys for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if<br />using a static access key. | | |
|
||||
|
||||
|
||||
#### StatefulSet
|
||||
|
||||
|
||||
@@ -496,6 +688,22 @@ _Appears in:_
|
||||
| `pod` _[Pod](#pod)_ | Configuration for the proxy Pod. | | |
|
||||
|
||||
|
||||
#### Storage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderSpec](#recorderspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `s3` _[S3](#s3)_ | Configure an S3-compatible API for storage. Required if the UI is not<br />enabled, to ensure that recordings are accessible. | | |
|
||||
|
||||
|
||||
#### SubnetRouter
|
||||
|
||||
|
||||
@@ -540,9 +748,28 @@ _Validation:_
|
||||
|
||||
_Appears in:_
|
||||
- [ConnectorSpec](#connectorspec)
|
||||
- [RecorderSpec](#recorderspec)
|
||||
|
||||
|
||||
|
||||
#### TailnetDevice
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [RecorderStatus](#recorderstatus)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `hostname` _string_ | Hostname is the fully qualified domain name of the device.<br />If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the<br />node. | | |
|
||||
| `tailnetIPs` _string array_ | TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6)<br />assigned to the device. | | |
|
||||
| `url` _string_ | URL where the UI is available if enabled for replaying recordings. This<br />will be an HTTPS MagicDNS URL. You must be connected to the same tailnet<br />as the recorder to access it. | | |
|
||||
|
||||
|
||||
#### TailscaleConfig
|
||||
|
||||
|
||||
|
@@ -49,7 +49,16 @@ func init() {
|
||||
|
||||
// Adds the list of known types to api.Scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion, &Connector{}, &ConnectorList{}, &ProxyClass{}, &ProxyClassList{}, &DNSConfig{}, &DNSConfigList{})
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Connector{},
|
||||
&ConnectorList{},
|
||||
&ProxyClass{},
|
||||
&ProxyClassList{},
|
||||
&DNSConfig{},
|
||||
&DNSConfigList{},
|
||||
&Recorder{},
|
||||
&RecorderList{},
|
||||
)
|
||||
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
@@ -173,4 +173,5 @@ const (
|
||||
ConnectorReady ConditionType = `ConnectorReady`
|
||||
ProxyClassready ConditionType = `ProxyClassReady`
|
||||
ProxyReady ConditionType = `TailscaleProxyReady` // a Tailscale-specific condition type for corev1.Service
|
||||
RecorderReady ConditionType = `RecorderReady`
|
||||
)
|
||||
|
249
k8s-operator/apis/v1alpha1/types_recorder.go
Normal file
249
k8s-operator/apis/v1alpha1/types_recorder.go
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !plan9
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster,shortName=rec
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=`.status.conditions[?(@.type == "RecorderReady")].reason`,description="Status of the deployed Recorder resources."
|
||||
// +kubebuilder:printcolumn:name="URL",type="string",JSONPath=`.status.devices[?(@.url != "")].url`,description="URL on which the UI is exposed if enabled."
|
||||
|
||||
type Recorder struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec describes the desired recorder instance.
|
||||
Spec RecorderSpec `json:"spec"`
|
||||
|
||||
// RecorderStatus describes the status of the recorder. This is set
|
||||
// and managed by the Tailscale operator.
|
||||
// +optional
|
||||
Status RecorderStatus `json:"status"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
type RecorderList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata"`
|
||||
|
||||
Items []Recorder `json:"items"`
|
||||
}
|
||||
|
||||
type RecorderSpec struct {
|
||||
// Configuration parameters for the Recorder's StatefulSet. The operator
|
||||
// deploys a StatefulSet for each Recorder resource.
|
||||
// +optional
|
||||
StatefulSet RecorderStatefulSet `json:"statefulSet"`
|
||||
|
||||
// Tags that the Tailscale device will be tagged with. Defaults to [tag:k8s].
|
||||
// If you specify custom tags here, make sure you also make the operator
|
||||
// an owner of these tags.
|
||||
// See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator.
|
||||
// Tags cannot be changed once a Recorder node has been created.
|
||||
// Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
|
||||
// +optional
|
||||
Tags Tags `json:"tags,omitempty"`
|
||||
|
||||
// TODO(tomhjp): Support a hostname or hostname prefix field, depending on
|
||||
// the plan for multiple replicas.
|
||||
|
||||
// Set to true to enable the Recorder UI. The UI lists and plays recorded sessions.
|
||||
// The UI will be served at <MagicDNS name of the recorder>:443. Defaults to false.
|
||||
// Corresponds to --ui tsrecorder flag https://tailscale.com/kb/1246/tailscale-ssh-session-recording#deploy-a-recorder-node.
|
||||
// Required if S3 storage is not set up, to ensure that recordings are accessible.
|
||||
// +optional
|
||||
EnableUI bool `json:"enableUI,omitempty"`
|
||||
|
||||
// Configure where to store session recordings. By default, recordings will
|
||||
// be stored in a local ephemeral volume, and will not be persisted past the
|
||||
// lifetime of a specific pod.
|
||||
// +optional
|
||||
Storage Storage `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
type RecorderStatefulSet struct {
|
||||
// Labels that will be added to the StatefulSet created for the Recorder.
|
||||
// Any labels specified here will be merged with the default labels applied
|
||||
// to the StatefulSet by the operator.
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations that will be added to the StatefulSet created for the Recorder.
|
||||
// Any Annotations specified here will be merged with the default annotations
|
||||
// applied to the StatefulSet by the operator.
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Configuration for pods created by the Recorder's StatefulSet.
|
||||
// +optional
|
||||
Pod RecorderPod `json:"pod,omitempty"`
|
||||
}
|
||||
|
||||
type RecorderPod struct {
|
||||
// Labels that will be added to Recorder Pods. Any labels specified here
|
||||
// will be merged with the default labels applied to the Pod by the operator.
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations that will be added to Recorder Pods. Any annotations
|
||||
// specified here will be merged with the default annotations applied to
|
||||
// the Pod by the operator.
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Affinity rules for Recorder Pods. By default, the operator does not
|
||||
// apply any affinity rules.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#affinity
|
||||
// +optional
|
||||
Affinity *corev1.Affinity `json:"affinity,omitempty"`
|
||||
|
||||
// Configuration for the Recorder container running tailscale.
|
||||
// +optional
|
||||
Container RecorderContainer `json:"container,omitempty"`
|
||||
|
||||
// Security context for Recorder Pods. By default, the operator does not
|
||||
// apply any Pod security context.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-2
|
||||
// +optional
|
||||
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
||||
|
||||
// Image pull Secrets for Recorder Pods.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec
|
||||
// +optional
|
||||
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
||||
|
||||
// Node selector rules for Recorder Pods. By default, the operator does
|
||||
// not apply any node selector rules.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling
|
||||
// +optional
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
|
||||
// Tolerations for Recorder Pods. By default, the operator does not apply
|
||||
// any tolerations.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling
|
||||
// +optional
|
||||
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
||||
}
|
||||
|
||||
type RecorderContainer struct {
|
||||
// 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
|
||||
Env []Env `json:"env,omitempty"`
|
||||
|
||||
// Container image name including tag. Defaults to docker.io/tailscale/tsrecorder
|
||||
// with the same tag as the operator, but the official images are also
|
||||
// available at ghcr.io/tailscale/tsrecorder.
|
||||
// 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, the operator does not apply any resource requirements. The
|
||||
// amount of resources required wil depend on the volume of recordings sent.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources
|
||||
// +optional
|
||||
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
||||
|
||||
// Container security context. By default, the operator does not apply any
|
||||
// container security context.
|
||||
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context
|
||||
// +optional
|
||||
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
// Configure an S3-compatible API for storage. Required if the UI is not
|
||||
// enabled, to ensure that recordings are accessible.
|
||||
// +optional
|
||||
S3 *S3 `json:"s3,omitempty"`
|
||||
}
|
||||
|
||||
type S3 struct {
|
||||
// S3-compatible endpoint, e.g. s3.us-east-1.amazonaws.com.
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
|
||||
// Bucket name to write to. The bucket is expected to be used solely for
|
||||
// recordings, as there is no stable prefix for written object names.
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
|
||||
// Configure environment variable credentials for managing objects in the
|
||||
// configured bucket. If not set, tsrecorder will try to acquire credentials
|
||||
// first from the file system and then the STS API.
|
||||
// +optional
|
||||
Credentials S3Credentials `json:"credentials,omitempty"`
|
||||
}
|
||||
|
||||
type S3Credentials struct {
|
||||
// Use a Kubernetes Secret from the operator's namespace as the source of
|
||||
// credentials.
|
||||
// +optional
|
||||
Secret S3Secret `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
type S3Secret struct {
|
||||
// The name of a Kubernetes Secret in the operator's namespace that contains
|
||||
// credentials for writing to the configured bucket. Each key-value pair
|
||||
// from the secret's data will be mounted as an environment variable. It
|
||||
// should include keys for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if
|
||||
// using a static access key.
|
||||
//+optional
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type RecorderStatus struct {
|
||||
// List of status conditions to indicate the status of the Recorder.
|
||||
// Known condition types are `RecorderReady`.
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// List of tailnet devices associated with the Recorder statefulset.
|
||||
// +listType=map
|
||||
// +listMapKey=hostname
|
||||
// +optional
|
||||
Devices []TailnetDevice `json:"devices,omitempty"`
|
||||
}
|
||||
|
||||
type TailnetDevice struct {
|
||||
// Hostname is the fully qualified domain name of the device.
|
||||
// If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the
|
||||
// node.
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
// TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6)
|
||||
// assigned to the device.
|
||||
// +optional
|
||||
TailnetIPs []string `json:"tailnetIPs,omitempty"`
|
||||
|
||||
// URL where the UI is available if enabled for replaying recordings. This
|
||||
// will be an HTTPS MagicDNS URL. You must be connected to the same tailnet
|
||||
// as the recorder to access it.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
@@ -78,16 +78,16 @@ type DNSConfigSpec struct {
|
||||
}
|
||||
|
||||
type Nameserver struct {
|
||||
// Nameserver image.
|
||||
// Nameserver image. Defaults to tailscale/k8s-nameserver:unstable.
|
||||
// +optional
|
||||
Image *Image `json:"image,omitempty"`
|
||||
Image *NameserverImage `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
type NameserverImage struct {
|
||||
// Repo defaults to tailscale/k8s-nameserver.
|
||||
// +optional
|
||||
Repo string `json:"repo,omitempty"`
|
||||
// Tag defaults to operator's own tag.
|
||||
// Tag defaults to unstable.
|
||||
// +optional
|
||||
Tag string `json:"tag,omitempty"`
|
||||
}
|
||||
|
@@ -271,21 +271,6 @@ func (in *Env) DeepCopy() *Env {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Image) DeepCopyInto(out *Image) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image.
|
||||
func (in *Image) DeepCopy() *Image {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Image)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Metrics) DeepCopyInto(out *Metrics) {
|
||||
*out = *in
|
||||
@@ -306,7 +291,7 @@ func (in *Nameserver) DeepCopyInto(out *Nameserver) {
|
||||
*out = *in
|
||||
if in.Image != nil {
|
||||
in, out := &in.Image, &out.Image
|
||||
*out = new(Image)
|
||||
*out = new(NameserverImage)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
@@ -321,6 +306,21 @@ func (in *Nameserver) DeepCopy() *Nameserver {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NameserverImage) DeepCopyInto(out *NameserverImage) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NameserverImage.
|
||||
func (in *NameserverImage) DeepCopy() *NameserverImage {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NameserverImage)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NameserverStatus) DeepCopyInto(out *NameserverStatus) {
|
||||
*out = *in
|
||||
@@ -515,6 +515,231 @@ func (in *ProxyClassStatus) DeepCopy() *ProxyClassStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Recorder) DeepCopyInto(out *Recorder) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Recorder.
|
||||
func (in *Recorder) DeepCopy() *Recorder {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Recorder)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Recorder) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderContainer) DeepCopyInto(out *RecorderContainer) {
|
||||
*out = *in
|
||||
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(corev1.SecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderContainer.
|
||||
func (in *RecorderContainer) DeepCopy() *RecorderContainer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderContainer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderList) DeepCopyInto(out *RecorderList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Recorder, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderList.
|
||||
func (in *RecorderList) DeepCopy() *RecorderList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *RecorderList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderPod) DeepCopyInto(out *RecorderPod) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Affinity != nil {
|
||||
in, out := &in.Affinity, &out.Affinity
|
||||
*out = new(corev1.Affinity)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Container.DeepCopyInto(&out.Container)
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(corev1.PodSecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ImagePullSecrets != nil {
|
||||
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
||||
*out = make([]corev1.LocalObjectReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Tolerations != nil {
|
||||
in, out := &in.Tolerations, &out.Tolerations
|
||||
*out = make([]corev1.Toleration, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderPod.
|
||||
func (in *RecorderPod) DeepCopy() *RecorderPod {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderPod)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderSpec) DeepCopyInto(out *RecorderSpec) {
|
||||
*out = *in
|
||||
in.StatefulSet.DeepCopyInto(&out.StatefulSet)
|
||||
if in.Tags != nil {
|
||||
in, out := &in.Tags, &out.Tags
|
||||
*out = make(Tags, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Storage.DeepCopyInto(&out.Storage)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderSpec.
|
||||
func (in *RecorderSpec) DeepCopy() *RecorderSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderStatefulSet) DeepCopyInto(out *RecorderStatefulSet) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.Pod.DeepCopyInto(&out.Pod)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderStatefulSet.
|
||||
func (in *RecorderStatefulSet) DeepCopy() *RecorderStatefulSet {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderStatefulSet)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RecorderStatus) DeepCopyInto(out *RecorderStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Devices != nil {
|
||||
in, out := &in.Devices, &out.Devices
|
||||
*out = make([]TailnetDevice, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecorderStatus.
|
||||
func (in *RecorderStatus) DeepCopy() *RecorderStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RecorderStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in Routes) DeepCopyInto(out *Routes) {
|
||||
{
|
||||
@@ -534,6 +759,53 @@ func (in Routes) DeepCopy() Routes {
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *S3) DeepCopyInto(out *S3) {
|
||||
*out = *in
|
||||
out.Credentials = in.Credentials
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3.
|
||||
func (in *S3) DeepCopy() *S3 {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(S3)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *S3Credentials) DeepCopyInto(out *S3Credentials) {
|
||||
*out = *in
|
||||
out.Secret = in.Secret
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3Credentials.
|
||||
func (in *S3Credentials) DeepCopy() *S3Credentials {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(S3Credentials)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *S3Secret) DeepCopyInto(out *S3Secret) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3Secret.
|
||||
func (in *S3Secret) DeepCopy() *S3Secret {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(S3Secret)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StatefulSet) DeepCopyInto(out *StatefulSet) {
|
||||
*out = *in
|
||||
@@ -568,6 +840,26 @@ func (in *StatefulSet) DeepCopy() *StatefulSet {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Storage) DeepCopyInto(out *Storage) {
|
||||
*out = *in
|
||||
if in.S3 != nil {
|
||||
in, out := &in.S3, &out.S3
|
||||
*out = new(S3)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage.
|
||||
func (in *Storage) DeepCopy() *Storage {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Storage)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SubnetRouter) DeepCopyInto(out *SubnetRouter) {
|
||||
*out = *in
|
||||
@@ -607,6 +899,26 @@ func (in Tags) DeepCopy() Tags {
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TailnetDevice) DeepCopyInto(out *TailnetDevice) {
|
||||
*out = *in
|
||||
if in.TailnetIPs != nil {
|
||||
in, out := &in.TailnetIPs, &out.TailnetIPs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TailnetDevice.
|
||||
func (in *TailnetDevice) DeepCopy() *TailnetDevice {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TailnetDevice)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TailscaleConfig) DeepCopyInto(out *TailscaleConfig) {
|
||||
*out = *in
|
||||
|
@@ -63,6 +63,14 @@ func RemoveServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionTy
|
||||
})
|
||||
}
|
||||
|
||||
// SetRecorderCondition ensures that Recorder status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes.
|
||||
func SetRecorderCondition(tsr *tsapi.Recorder, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
conds := updateCondition(tsr.Status.Conditions, conditionType, status, reason, message, gen, clock, logger)
|
||||
tsr.Status.Conditions = conds
|
||||
}
|
||||
|
||||
func updateCondition(conds []metav1.Condition, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) []metav1.Condition {
|
||||
newCondition := metav1.Condition{
|
||||
Type: string(conditionType),
|
||||
|
Reference in New Issue
Block a user