diff --git a/PROJECT b/PROJECT index 7dfe0d1..68b0780 100644 --- a/PROJECT +++ b/PROJECT @@ -107,4 +107,12 @@ resources: kind: NetworkResource path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: netbird.io + kind: SidecarProfile + path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/sidecarprofile_types.go b/api/v1alpha1/sidecarprofile_types.go new file mode 100644 index 0000000..f15f768 --- /dev/null +++ b/api/v1alpha1/sidecarprofile_types.go @@ -0,0 +1,102 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// InjectionMode defines how the sidecar is injected into the pod. +// +kubebuilder:validation:Enum=Sidecar;Container +type InjectionMode string + +const ( + // InjectionModeSidecar injects the client as a sidecar container. + InjectionModeSidecar InjectionMode = "Sidecar" + + // InjectionModeContainer injects the client as a regular container. + InjectionModeContainer InjectionMode = "Container" +) + +// SidecarProfileSpec defines the desired state of SidecarProfile. +type SidecarProfileSpec struct { + // SetupKeyRef is the reference to the setup key used in the client. + // +required + SetupKeyRef corev1.LocalObjectReference `json:"setupKeyRef"` + + // PodSelector determines which pods the profile should apply to. + // An empty slector means the profile will apply to all pods in the namespace. + // +optional + PodSelector *metav1.LabelSelector `json:"podSelector,omitempty"` + + // InjectionMode defines whether the sidecar is injected as a native Kubernetes sidecar container or as a regular container. + // +kubebuilder:default=Sidecar + // +optional + InjectionMode InjectionMode `json:"injectionMode,omitempty"` + + // ExtraDNSLabels assigns additional DNS names to peers beyond their default hostname. + // +optional + ExtraDNSLabels []string `json:"extraDNSLabels,omitempty"` + + // +optional + ContainerOverride *ContainerOverride `json:"containerOverride,omitempty"` +} + +type ContainerOverride struct { + // Image overrides the image used by the client. + // +optional + Image string `json:"image,omitempty"` + + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // +optional + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` +} + +// SidecarProfileStatus defines the observed state of SidecarProfile. +type SidecarProfileStatus struct { + // Conditions holds the conditions for the SidecarProfile. + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource + +// SidecarProfile is the Schema for the sidecarprofiles API. +type SidecarProfile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +required + Spec SidecarProfileSpec `json:"spec"` + + // +kubebuilder:default={} + Status SidecarProfileStatus `json:"status,omitempty"` +} + +// GetConditions returns the status conditions of the object. +func (s *SidecarProfile) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (s *SidecarProfile) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// SidecarProfileList contains a list of SidecarProfile +type SidecarProfileList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitzero"` + Items []SidecarProfile `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SidecarProfile{}, &SidecarProfileList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ff93134..2f35b21 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -10,6 +10,33 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerOverride) DeepCopyInto(out *ContainerOverride) { + *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + 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 ContainerOverride. +func (in *ContainerOverride) DeepCopy() *ContainerOverride { + if in == nil { + return nil + } + out := new(ContainerOverride) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CrossNamespaceReference) DeepCopyInto(out *CrossNamespaceReference) { *out = *in @@ -481,6 +508,118 @@ func (in *SetupKeyStatus) DeepCopy() *SetupKeyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarProfile) DeepCopyInto(out *SidecarProfile) { + *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 SidecarProfile. +func (in *SidecarProfile) DeepCopy() *SidecarProfile { + if in == nil { + return nil + } + out := new(SidecarProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SidecarProfile) 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 *SidecarProfileList) DeepCopyInto(out *SidecarProfileList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SidecarProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarProfileList. +func (in *SidecarProfileList) DeepCopy() *SidecarProfileList { + if in == nil { + return nil + } + out := new(SidecarProfileList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SidecarProfileList) 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 *SidecarProfileSpec) DeepCopyInto(out *SidecarProfileSpec) { + *out = *in + out.SetupKeyRef = in.SetupKeyRef + if in.PodSelector != nil { + in, out := &in.PodSelector, &out.PodSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.ExtraDNSLabels != nil { + in, out := &in.ExtraDNSLabels, &out.ExtraDNSLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ContainerOverride != nil { + in, out := &in.ContainerOverride, &out.ContainerOverride + *out = new(ContainerOverride) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarProfileSpec. +func (in *SidecarProfileSpec) DeepCopy() *SidecarProfileSpec { + if in == nil { + return nil + } + out := new(SidecarProfileSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarProfileStatus) DeepCopyInto(out *SidecarProfileStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarProfileStatus. +func (in *SidecarProfileStatus) DeepCopy() *SidecarProfileStatus { + if in == nil { + return nil + } + out := new(SidecarProfileStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadOverride) DeepCopyInto(out *WorkloadOverride) { *out = *in diff --git a/examples/refactor/sidecarprofile.yaml b/examples/refactor/sidecarprofile.yaml new file mode 100644 index 0000000..f56cedb --- /dev/null +++ b/examples/refactor/sidecarprofile.yaml @@ -0,0 +1,34 @@ +apiVersion: netbird.io/v1alpha1 +kind: SetupKey +metadata: + name: sidecar + namespace: default +spec: + name: sidecar + ephemeral: true +--- +apiVersion: netbird.io/v1alpha1 +kind: SidecarProfile +metadata: + name: test + namespace: default +spec: + setupKeyRef: + name: sidecar + podSelector: + matchLabels: + app: ubuntu +--- +apiVersion: v1 +kind: Pod +metadata: + name: ubuntu + namespace: default + labels: + app: ubuntu +spec: + containers: + - name: ubuntu + image: ubuntu:latest + command: ["sleep", "infinity"] + diff --git a/helm/kubernetes-operator/crds/netbird.io_sidecarprofiles.yaml b/helm/kubernetes-operator/crds/netbird.io_sidecarprofiles.yaml new file mode 100644 index 0000000..d08db01 --- /dev/null +++ b/helm/kubernetes-operator/crds/netbird.io_sidecarprofiles.yaml @@ -0,0 +1,551 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: sidecarprofiles.netbird.io +spec: + group: netbird.io + names: + kind: SidecarProfile + listKind: SidecarProfileList + plural: sidecarprofiles + singular: sidecarprofile + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: SidecarProfile is the Schema for the sidecarprofiles API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SidecarProfileSpec defines the desired state of SidecarProfile. + properties: + containerOverride: + properties: + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + 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 + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image overrides the image used by the client. + type: string + securityContext: + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. When both + are set, the values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + type: object + extraDNSLabels: + description: ExtraDNSLabels assigns additional DNS names to peers + beyond their default hostname. + items: + type: string + type: array + injectionMode: + default: Sidecar + description: InjectionMode defines whether the sidecar is injected + as a native Kubernetes sidecar container or as a regular container. + enum: + - Sidecar + - Container + type: string + podSelector: + description: |- + PodSelector determines which pods the profile should apply to. + An empty slector means the profile will apply to all pods in the namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + setupKeyRef: + description: SetupKeyRef is the reference to the setup key used in + the client. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - setupKeyRef + type: object + status: + default: {} + description: SidecarProfileStatus defines the observed state of SidecarProfile. + properties: + conditions: + description: Conditions holds the conditions for the SidecarProfile. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/kubernetes-operator/templates/rbac.yaml b/helm/kubernetes-operator/templates/rbac.yaml index ad12f47..9da9cc0 100644 --- a/helm/kubernetes-operator/templates/rbac.yaml +++ b/helm/kubernetes-operator/templates/rbac.yaml @@ -27,7 +27,6 @@ rules: - get - patch - update -{{- if or .Values.netbirdAPI.key .Values.netbirdAPI.keyFromSecret }} - apiGroups: - netbird.io resources: @@ -35,6 +34,11 @@ rules: - nbresources - nbroutingpeers - nbpolicies + - setupkeys + - groups + - networkrouters + - networkresources + - sidecarprofiles verbs: - get - patch @@ -50,6 +54,11 @@ rules: - nbresources/status - nbroutingpeers/status - nbpolicies/status + - setupkeys/status + - groups/status + - networkrouters/status + - networkresources/status + - sidecarprofiles/status verbs: - get - patch @@ -61,6 +70,11 @@ rules: - nbresources/finalizers - nbroutingpeers/finalizers - nbpolicies/finalizers + - setupkeys/finalizers + - groups/finalizers + - networkrouters/finalizers + - networkresources/finalizers + - sidecarprofiles/finalizers verbs: - update - apiGroups: @@ -99,7 +113,6 @@ rules: - watch - create - delete -{{- end }} - apiGroups: - "" resources: diff --git a/internal/webhook/v1/pod_webhook.go b/internal/webhook/v1/pod_webhook.go index 640d06d..73ce861 100644 --- a/internal/webhook/v1/pod_webhook.go +++ b/internal/webhook/v1/pod_webhook.go @@ -17,20 +17,31 @@ limitations under the License. package v1 import ( + "cmp" "context" + "encoding/json" "fmt" + "slices" + "strings" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + "github.com/netbirdio/kubernetes-operator/internal/controller" ) const ( + SidecarProfileAnnotation = "netbird.io/sidecar-profile" + setupKeyAnnotation = "netbird.io/setup-key" sidecarAnnotation = "netbird.io/init-sidecar" ) @@ -60,14 +71,128 @@ type PodNetbirdInjector struct { var _ admission.Defaulter[*corev1.Pod] = &PodNetbirdInjector{} -// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind Pod. func (d *PodNetbirdInjector) Default(ctx context.Context, pod *corev1.Pod) error { - podlog.Info("Defaulting for Pod", "name", pod.GetName()) + // If setup key annotations are set we do the legacy injection. + if pod.Annotations != nil && pod.Annotations[setupKeyAnnotation] != "" { + return d.legacyInjector(ctx, pod) + } - // if the setup key annotation is missing, do nothing. - if pod.Annotations == nil || pod.Annotations[setupKeyAnnotation] == "" { + // Find sidecar profiles matching pods labels. + sidecarProfileList := &nbv1alpha1.SidecarProfileList{} + err := d.client.List(ctx, sidecarProfileList, client.InNamespace(pod.Namespace)) + if err != nil { + return err + } + sidecarProfiles := []nbv1alpha1.SidecarProfile{} + for _, sidecarProfile := range sidecarProfileList.Items { + if sidecarProfile.Spec.PodSelector == nil || sidecarProfile.Spec.PodSelector.Size() == 0 { + sidecarProfiles = append(sidecarProfiles, sidecarProfile) + continue + } + selector, err := metav1.LabelSelectorAsSelector(sidecarProfile.Spec.PodSelector) + if err != nil { + return err + } + if selector.Matches(labels.Set(pod.Labels)) { + sidecarProfiles = append(sidecarProfiles, sidecarProfile) + } + } + // Do nothing if no profile matches. + if len(sidecarProfiles) == 0 { return nil } + // If two match we chose the first in alphabetical order. + if len(sidecarProfiles) > 1 { + slices.SortFunc(sidecarProfiles, func(a, b nbv1alpha1.SidecarProfile) int { + return cmp.Compare(a.Name, b.Name) + }) + } + sidecarProfile := sidecarProfiles[0] + + // Get setup key referenced by sidecar profile. + setupKey := &nbv1alpha1.SetupKey{ + ObjectMeta: metav1.ObjectMeta{ + Name: sidecarProfile.Spec.SetupKeyRef.Name, + Namespace: pod.Namespace, + }, + } + err = d.client.Get(ctx, client.ObjectKeyFromObject(setupKey), setupKey) + if err != nil { + return err + } + + // Add sidecar container. + envVars := []corev1.EnvVar{ + { + Name: "NB_SETUP_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: setupKey.SecretName(), + }, + Key: controller.SetupKeySecretKey, + }, + }, + }, + { + Name: "NB_MANAGEMENT_URL", + Value: d.managementURL, + }, + } + if len(sidecarProfile.Spec.ExtraDNSLabels) > 0 { + envVars = append(envVars, corev1.EnvVar{ + Name: "NB_EXTRA_DNS_LABELS", + Value: strings.Join(sidecarProfile.Spec.ExtraDNSLabels, ","), + }) + } + + container := corev1.Container{ + Name: "netbird", + Image: d.clientImage, + Env: envVars, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"NET_ADMIN"}, + }, + }, + } + if sidecarProfile.Spec.ContainerOverride != nil { + baseJSON, err := json.Marshal(&container) + if err != nil { + return err + } + overrideJSON, err := json.Marshal(sidecarProfile.Spec.ContainerOverride) + if err != nil { + return err + } + mergedJSON, err := strategicpatch.StrategicMergePatch(baseJSON, overrideJSON, corev1.Container{}) + if err != nil { + return err + } + err = json.Unmarshal(mergedJSON, &container) + if err != nil { + return err + } + } + + switch sidecarProfile.Spec.InjectionMode { + case nbv1alpha1.InjectionModeSidecar: + restartPolicy := corev1.ContainerRestartPolicyAlways + container.RestartPolicy = &restartPolicy + pod.Spec.InitContainers = append(pod.Spec.InitContainers, container) + case nbv1alpha1.InjectionModeContainer: + pod.Spec.Containers = append(pod.Spec.Containers, container) + default: + return fmt.Errorf("unknown injection mode %s", sidecarProfile.Spec.InjectionMode) + } + + pod.Annotations[SidecarProfileAnnotation] = sidecarProfile.Name + + return nil +} + +func (d *PodNetbirdInjector) legacyInjector(ctx context.Context, pod *corev1.Pod) error { + podlog.Info("Defaulting for Pod", "name", pod.GetName()) // retrieve the NBSetupKey resource var nbSetupKey netbirdiov1.NBSetupKey @@ -118,7 +243,17 @@ func (d *PodNetbirdInjector) Default(ctx context.Context, pod *corev1.Pod) error } // Build the netbird container spec. - nbContainer := d.buildNetbirdContainer(envVars, nbSetupKey.Spec.VolumeMounts) + nbContainer := corev1.Container{ + Name: "netbird", + Image: d.clientImage, + Env: envVars, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"NET_ADMIN"}, + }, + }, + VolumeMounts: nbSetupKey.Spec.VolumeMounts, + } // If sidecar mode is requested, inject as a sidecar (init container with restartPolicy: Always). if pod.Annotations[sidecarAnnotation] == "true" { @@ -130,22 +265,5 @@ func (d *PodNetbirdInjector) Default(ctx context.Context, pod *corev1.Pod) error } pod.Spec.Volumes = append(pod.Spec.Volumes, nbSetupKey.Spec.Volumes...) - return nil } - -// buildNetbirdContainer constructs the NetBird container spec with the given -// environment variables and volume mounts. -func (d *PodNetbirdInjector) buildNetbirdContainer(envVars []corev1.EnvVar, volumeMounts []corev1.VolumeMount) corev1.Container { - return corev1.Container{ - Name: "netbird", - Image: d.clientImage, - Env: envVars, - SecurityContext: &corev1.SecurityContext{ - Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{"NET_ADMIN"}, - }, - }, - VolumeMounts: volumeMounts, - } -} diff --git a/internal/webhook/v1/pod_webhook_test.go b/internal/webhook/v1/pod_webhook_test.go index b67c80d..f74405d 100644 --- a/internal/webhook/v1/pod_webhook_test.go +++ b/internal/webhook/v1/pod_webhook_test.go @@ -20,11 +20,11 @@ import ( "context" netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var _ = Describe("Pod Webhook", func() { @@ -35,7 +35,7 @@ var _ = Describe("Pod Webhook", func() { BeforeEach(func() { obj = &corev1.Pod{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", Annotations: make(map[string]string), @@ -83,7 +83,7 @@ var _ = Describe("Pod Webhook", func() { When("NBSetupKey exists", Ordered, func() { BeforeAll(func() { sk := netbirdiov1.NBSetupKey{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", }, @@ -98,7 +98,7 @@ var _ = Describe("Pod Webhook", func() { } err := k8sClient.Create(context.Background(), &corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "test", }, }) @@ -154,4 +154,59 @@ var _ = Describe("Pod Webhook", func() { }) }) + + Context("When creating Pod with SidecarProfile", func() { + BeforeEach(func() { + sidecarProfile := &nbv1alpha1.SidecarProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: nbv1alpha1.SidecarProfileSpec{ + SetupKeyRef: corev1.LocalObjectReference{ + Name: "test", + }, + InjectionMode: nbv1alpha1.InjectionModeContainer, + }, + } + Expect(k8sClient.Create(context.Background(), sidecarProfile)).To(Succeed()) + }) + + AfterEach(func() { + sidecarProfile := &nbv1alpha1.SidecarProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + } + Expect(k8sClient.Delete(ctx, sidecarProfile)).To(Succeed()) + }) + + When("SetupKey doesn't exist", func() { + It("Should fail", func() { + Expect(defaulter.Default(context.Background(), obj)).To(HaveOccurred()) + Expect(obj.Spec.Containers).To(HaveLen(1)) + }) + }) + + When("SetupKey exists", func() { + It("Should succeed", func() { + setupKey := &nbv1alpha1.SetupKey{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: nbv1alpha1.SetupKeySpec{ + Name: "test", + Ephemeral: true, + }, + } + Expect(k8sClient.Create(context.Background(), setupKey)).To(Succeed()) + + Expect(defaulter.Default(context.Background(), obj)).NotTo(HaveOccurred()) + Expect(obj.Spec.Containers).To(HaveLen(2)) + Expect(obj.Spec.Containers[1].Name).To(Equal("netbird")) + }) + }) + }) }) diff --git a/internal/webhook/v1/webhook_suite_test.go b/internal/webhook/v1/webhook_suite_test.go index 59c3a8a..023b8f1 100644 --- a/internal/webhook/v1/webhook_suite_test.go +++ b/internal/webhook/v1/webhook_suite_test.go @@ -42,6 +42,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" // +kubebuilder:scaffold:imports ) @@ -78,6 +79,9 @@ var _ = BeforeSuite(func() { err = netbirdiov1.AddToScheme(scheme) Expect(err).NotTo(HaveOccurred()) + err = nbv1alpha1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme By("bootstrapping test environment") diff --git a/pkg/applyconfigurations/api/v1alpha1/containeroverride.go b/pkg/applyconfigurations/api/v1alpha1/containeroverride.go new file mode 100644 index 0000000..088ae89 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/containeroverride.go @@ -0,0 +1,48 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" +) + +// ContainerOverrideApplyConfiguration represents a declarative configuration of the ContainerOverride type for use +// with apply. +type ContainerOverrideApplyConfiguration struct { + // Image overrides the image used by the client. + Image *string `json:"image,omitempty"` + Env []v1.EnvVar `json:"env,omitempty"` + SecurityContext *v1.SecurityContext `json:"securityContext,omitempty"` +} + +// ContainerOverrideApplyConfiguration constructs a declarative configuration of the ContainerOverride type for use with +// apply. +func ContainerOverride() *ContainerOverrideApplyConfiguration { + return &ContainerOverrideApplyConfiguration{} +} + +// WithImage sets the Image field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Image field is set to the value of the last call. +func (b *ContainerOverrideApplyConfiguration) WithImage(value string) *ContainerOverrideApplyConfiguration { + b.Image = &value + return b +} + +// WithEnv adds the given value to the Env field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Env field. +func (b *ContainerOverrideApplyConfiguration) WithEnv(values ...v1.EnvVar) *ContainerOverrideApplyConfiguration { + for i := range values { + b.Env = append(b.Env, values[i]) + } + return b +} + +// WithSecurityContext sets the SecurityContext field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SecurityContext field is set to the value of the last call. +func (b *ContainerOverrideApplyConfiguration) WithSecurityContext(value v1.SecurityContext) *ContainerOverrideApplyConfiguration { + b.SecurityContext = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/sidecarprofile.go b/pkg/applyconfigurations/api/v1alpha1/sidecarprofile.go new file mode 100644 index 0000000..eecbe58 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/sidecarprofile.go @@ -0,0 +1,229 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// SidecarProfileApplyConfiguration represents a declarative configuration of the SidecarProfile type for use +// with apply. +// +// SidecarProfile is the Schema for the sidecarprofiles API. +type SidecarProfileApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *SidecarProfileSpecApplyConfiguration `json:"spec,omitempty"` + Status *SidecarProfileStatusApplyConfiguration `json:"status,omitempty"` +} + +// SidecarProfile constructs a declarative configuration of the SidecarProfile type for use with +// apply. +func SidecarProfile(name, namespace string) *SidecarProfileApplyConfiguration { + b := &SidecarProfileApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("SidecarProfile") + b.WithAPIVersion("netbird.io/v1alpha1") + return b +} + +func (b SidecarProfileApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithKind(value string) *SidecarProfileApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithAPIVersion(value string) *SidecarProfileApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithName(value string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithGenerateName(value string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithNamespace(value string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithUID(value types.UID) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithResourceVersion(value string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithGeneration(value int64) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithCreationTimestamp(value metav1.Time) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *SidecarProfileApplyConfiguration) WithLabels(entries map[string]string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *SidecarProfileApplyConfiguration) WithAnnotations(entries map[string]string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *SidecarProfileApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *SidecarProfileApplyConfiguration) WithFinalizers(values ...string) *SidecarProfileApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *SidecarProfileApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithSpec(value *SidecarProfileSpecApplyConfiguration) *SidecarProfileApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *SidecarProfileApplyConfiguration) WithStatus(value *SidecarProfileStatusApplyConfiguration) *SidecarProfileApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *SidecarProfileApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *SidecarProfileApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *SidecarProfileApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *SidecarProfileApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/applyconfigurations/api/v1alpha1/sidecarprofilespec.go b/pkg/applyconfigurations/api/v1alpha1/sidecarprofilespec.go new file mode 100644 index 0000000..dcbf6fb --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/sidecarprofilespec.go @@ -0,0 +1,74 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// SidecarProfileSpecApplyConfiguration represents a declarative configuration of the SidecarProfileSpec type for use +// with apply. +// +// SidecarProfileSpec defines the desired state of SidecarProfile. +type SidecarProfileSpecApplyConfiguration struct { + // SetupKeyRef is the reference to the setup key used in the client. + SetupKeyRef *v1.LocalObjectReference `json:"setupKeyRef,omitempty"` + // PodSelector determines which pods the profile should apply to. + // An empty slector means the profile will apply to all pods in the namespace. + PodSelector *metav1.LabelSelectorApplyConfiguration `json:"podSelector,omitempty"` + // InjectionMode defines whether the sidecar is injected as a native Kubernetes sidecar container or as a regular container. + InjectionMode *apiv1alpha1.InjectionMode `json:"injectionMode,omitempty"` + // ExtraDNSLabels assigns additional DNS names to peers beyond their default hostname. + ExtraDNSLabels []string `json:"extraDNSLabels,omitempty"` + ContainerOverride *ContainerOverrideApplyConfiguration `json:"containerOverride,omitempty"` +} + +// SidecarProfileSpecApplyConfiguration constructs a declarative configuration of the SidecarProfileSpec type for use with +// apply. +func SidecarProfileSpec() *SidecarProfileSpecApplyConfiguration { + return &SidecarProfileSpecApplyConfiguration{} +} + +// WithSetupKeyRef sets the SetupKeyRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SetupKeyRef field is set to the value of the last call. +func (b *SidecarProfileSpecApplyConfiguration) WithSetupKeyRef(value v1.LocalObjectReference) *SidecarProfileSpecApplyConfiguration { + b.SetupKeyRef = &value + return b +} + +// WithPodSelector sets the PodSelector field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PodSelector field is set to the value of the last call. +func (b *SidecarProfileSpecApplyConfiguration) WithPodSelector(value *metav1.LabelSelectorApplyConfiguration) *SidecarProfileSpecApplyConfiguration { + b.PodSelector = value + return b +} + +// WithInjectionMode sets the InjectionMode field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InjectionMode field is set to the value of the last call. +func (b *SidecarProfileSpecApplyConfiguration) WithInjectionMode(value apiv1alpha1.InjectionMode) *SidecarProfileSpecApplyConfiguration { + b.InjectionMode = &value + return b +} + +// WithExtraDNSLabels adds the given value to the ExtraDNSLabels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ExtraDNSLabels field. +func (b *SidecarProfileSpecApplyConfiguration) WithExtraDNSLabels(values ...string) *SidecarProfileSpecApplyConfiguration { + for i := range values { + b.ExtraDNSLabels = append(b.ExtraDNSLabels, values[i]) + } + return b +} + +// WithContainerOverride sets the ContainerOverride field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ContainerOverride field is set to the value of the last call. +func (b *SidecarProfileSpecApplyConfiguration) WithContainerOverride(value *ContainerOverrideApplyConfiguration) *SidecarProfileSpecApplyConfiguration { + b.ContainerOverride = value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/sidecarprofilestatus.go b/pkg/applyconfigurations/api/v1alpha1/sidecarprofilestatus.go new file mode 100644 index 0000000..3581316 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/sidecarprofilestatus.go @@ -0,0 +1,35 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// SidecarProfileStatusApplyConfiguration represents a declarative configuration of the SidecarProfileStatus type for use +// with apply. +// +// SidecarProfileStatus defines the observed state of SidecarProfile. +type SidecarProfileStatusApplyConfiguration struct { + // Conditions holds the conditions for the SidecarProfile. + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// SidecarProfileStatusApplyConfiguration constructs a declarative configuration of the SidecarProfileStatus type for use with +// apply. +func SidecarProfileStatus() *SidecarProfileStatusApplyConfiguration { + return &SidecarProfileStatusApplyConfiguration{} +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *SidecarProfileStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *SidecarProfileStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} diff --git a/pkg/applyconfigurations/utils.go b/pkg/applyconfigurations/utils.go index 073a785..6091592 100644 --- a/pkg/applyconfigurations/utils.go +++ b/pkg/applyconfigurations/utils.go @@ -16,6 +16,8 @@ import ( func ForKind(kind schema.GroupVersionKind) interface{} { switch kind { // Group=netbird.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithKind("ContainerOverride"): + return &apiv1alpha1.ContainerOverrideApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("CrossNamespaceReference"): return &apiv1alpha1.CrossNamespaceReferenceApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("DNSZoneReference"): @@ -46,6 +48,12 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.SetupKeySpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SetupKeyStatus"): return &apiv1alpha1.SetupKeyStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SidecarProfile"): + return &apiv1alpha1.SidecarProfileApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SidecarProfileSpec"): + return &apiv1alpha1.SidecarProfileSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SidecarProfileStatus"): + return &apiv1alpha1.SidecarProfileStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("WorkloadOverride"): return &apiv1alpha1.WorkloadOverrideApplyConfiguration{}