diff --git a/apis/core/v1alpha1/flagservice_types.go b/apis/core/v1alpha1/flagservice_types.go index 5607ec83a..8f0dd6c60 100644 --- a/apis/core/v1alpha1/flagservice_types.go +++ b/apis/core/v1alpha1/flagservice_types.go @@ -28,10 +28,13 @@ import ( type FlagServiceSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - corev1.ServiceSpec `json:",inline"` - FlagSourceConfiguration string `json:"flagSourceConfiguration"` // +optional - ServiceAccountName string `json:"serviceAccountName"` + ServiceSpec corev1.ServiceSpec `json:"serviceSpec"` + //// +optional + //DeploymentSpec appsV1.DeploymentSpec `json:"deploymentSpec"` + // +optional + ServiceAccountName string `json:"serviceAccountName"` + FlagSourceConfiguration string `json:"flagSourceConfiguration"` } // FlagServiceStatus defines the observed state of FlagService diff --git a/config/crd/bases/core.openfeature.dev_flagservices.yaml b/config/crd/bases/core.openfeature.dev_flagservices.yaml index e69e72c87..392822f93 100644 --- a/config/crd/bases/core.openfeature.dev_flagservices.yaml +++ b/config/crd/bases/core.openfeature.dev_flagservices.yaml @@ -35,311 +35,332 @@ spec: spec: description: FlagServiceSpec defines the desired state of FlagService properties: - allocateLoadBalancerNodePorts: - description: allocateLoadBalancerNodePorts defines if NodePorts will - be automatically allocated for services with type LoadBalancer. Default - is "true". It may be set to "false" if the cluster load-balancer - does not rely on NodePorts. If the caller requests specific NodePorts - (by specifying a value), those requests will be respected, regardless - of this field. This field may only be set for services with type - LoadBalancer and will be cleared if the type is changed to any other - type. - type: boolean - clusterIP: - description: 'clusterIP is the IP address of the service and is usually - assigned randomly. If an address is specified manually, is in-range - (as per system configuration), and is not in use, it will be allocated - to the service; otherwise creation of the service will fail. This - field may not be changed through updates unless the type field is - also being changed to ExternalName (which requires this field to - be blank) or the type field is being changed from ExternalName (in - which case this field may optionally be specified, as describe above). Valid - values are "None", empty string (""), or a valid IP address. Setting - this to "None" makes a "headless service" (no virtual IP), which - is useful when direct endpoint connections are preferred and proxying - is not required. Only applies to types ClusterIP, NodePort, and - LoadBalancer. If this field is specified when creating a Service - of type ExternalName, creation will fail. This field will be wiped - when updating a Service to type ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' - type: string - clusterIPs: - description: "ClusterIPs is a list of IP addresses assigned to this - service, and are usually assigned randomly. If an address is specified - manually, is in-range (as per system configuration), and is not - in use, it will be allocated to the service; otherwise creation - of the service will fail. This field may not be changed through - updates unless the type field is also being changed to ExternalName - (which requires this field to be empty) or the type field is being - changed from ExternalName (in which case this field may optionally - be specified, as describe above). Valid values are \"None\", empty - string (\"\"), or a valid IP address. Setting this to \"None\" - makes a \"headless service\" (no virtual IP), which is useful when - direct endpoint connections are preferred and proxying is not required. - \ Only applies to types ClusterIP, NodePort, and LoadBalancer. If - this field is specified when creating a Service of type ExternalName, - creation will fail. This field will be wiped when updating a Service - to type ExternalName. If this field is not specified, it will be - initialized from the clusterIP field. If this field is specified, - clients must ensure that clusterIPs[0] and clusterIP have the same - value. \n This field may hold a maximum of two entries (dual-stack - IPs, in either order). These IPs must correspond to the values of - the ipFamilies field. Both clusterIPs and ipFamilies are governed - by the ipFamilyPolicy field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies" - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - description: externalIPs is a list of IP addresses for which nodes - in the cluster will also accept traffic for this service. These - IPs are not managed by Kubernetes. The user is responsible for - ensuring that traffic arrives at a node with this IP. A common - example is external load-balancers that are not part of the Kubernetes - system. - items: - type: string - type: array - externalName: - description: externalName is the external reference that discovery - mechanisms will return as an alias for this service (e.g. a DNS - CNAME record). No proxying will be involved. Must be a lowercase - RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires - `type` to be "ExternalName". - type: string - externalTrafficPolicy: - description: externalTrafficPolicy describes how nodes distribute - service traffic they receive on one of the Service's "externally-facing" - addresses (NodePorts, ExternalIPs, and LoadBalancer IPs). If set - to "Local", the proxy will configure the service in a way that assumes - that external load balancers will take care of balancing the service - traffic between nodes, and so each node will deliver traffic only - to the node-local endpoints of the service, without masquerading - the client source IP. (Traffic mistakenly sent to a node with no - endpoints will be dropped.) The default value, "Cluster", uses the - standard behavior of routing to all endpoints evenly (possibly modified - by topology and other features). Note that traffic sent to an External - IP or LoadBalancer IP from within the cluster will always get "Cluster" - semantics, but clients sending to a NodePort from within the cluster - may need to take traffic policy into account when picking a node. - type: string flagSourceConfiguration: type: string - healthCheckNodePort: - description: healthCheckNodePort specifies the healthcheck nodePort - for the service. This only applies when type is set to LoadBalancer - and externalTrafficPolicy is set to Local. If a value is specified, - is in-range, and is not in use, it will be used. If not specified, - a value will be automatically allocated. External systems (e.g. - load-balancers) can use this port to determine if a given node holds - endpoints for this service or not. If this field is specified when - creating a Service which does not need it, creation will fail. This - field will be wiped when updating a Service to no longer need it - (e.g. changing type). This field cannot be updated once set. - format: int32 - type: integer - internalTrafficPolicy: - description: InternalTrafficPolicy describes how nodes distribute - service traffic they receive on the ClusterIP. If set to "Local", - the proxy will assume that pods only want to talk to endpoints of - the service on the same node as the pod, dropping the traffic if - there are no local endpoints. The default value, "Cluster", uses - the standard behavior of routing to all endpoints evenly (possibly - modified by topology and other features). - type: string - ipFamilies: - description: "IPFamilies is a list of IP families (e.g. IPv4, IPv6) - assigned to this service. This field is usually assigned automatically - based on cluster configuration and the ipFamilyPolicy field. If - this field is specified manually, the requested family is available - in the cluster, and ipFamilyPolicy allows it, it will be used; otherwise - creation of the service will fail. This field is conditionally mutable: - it allows for adding or removing a secondary IP family, but it does - not allow changing the primary IP family of the Service. Valid values - are \"IPv4\" and \"IPv6\". This field only applies to Services - of types ClusterIP, NodePort, and LoadBalancer, and does apply to - \"headless\" services. This field will be wiped when updating a - Service to type ExternalName. \n This field may hold a maximum of - two entries (dual-stack families, in either order). These families - must correspond to the values of the clusterIPs field, if specified. - Both clusterIPs and ipFamilies are governed by the ipFamilyPolicy - field." - items: - description: IPFamily represents the IP Family (IPv4 or IPv6). This - type is used to express the family of an IP expressed by a type - (e.g. service.spec.ipFamilies). - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - description: IPFamilyPolicy represents the dual-stack-ness requested - or required by this Service. If there is no value provided, then - this field will be set to SingleStack. Services can be "SingleStack" - (a single IP family), "PreferDualStack" (two IP families on dual-stack - configured clusters or a single IP family on single-stack clusters), - or "RequireDualStack" (two IP families on dual-stack configured - clusters, otherwise fail). The ipFamilies and clusterIPs fields - depend on the value of this field. This field will be wiped when - updating a service to type ExternalName. - type: string - loadBalancerClass: - description: loadBalancerClass is the class of the load balancer implementation - this Service belongs to. If specified, the value of this field must - be a label-style identifier, with an optional prefix, e.g. "internal-vip" - or "example.com/internal-vip". Unprefixed names are reserved for - end-users. This field can only be set when the Service type is 'LoadBalancer'. - If not set, the default load balancer implementation is used, today - this is typically done through the cloud provider integration, but - should apply for any default implementation. If set, it is assumed - that a load balancer implementation is watching for Services with - a matching class. Any default load balancer implementation (e.g. - cloud providers) should ignore Services that set this field. This - field can only be set when creating or updating a Service to type - 'LoadBalancer'. Once set, it can not be changed. This field will - be wiped when a service is updated to a non 'LoadBalancer' type. - type: string - loadBalancerIP: - description: 'Only applies to Service Type: LoadBalancer. This feature - depends on whether the underlying cloud-provider supports specifying - the loadBalancerIP when a load balancer is created. This field will - be ignored if the cloud-provider does not support the feature. Deprecated: - This field was under-specified and its meaning varies across implementations, - and it cannot support dual-stack. As of Kubernetes v1.24, users - are encouraged to use implementation-specific annotations when available. - This field may be removed in a future API version.' + serviceAccountName: + description: // +optional DeploymentSpec appsV1.DeploymentSpec `json:"deploymentSpec"` type: string - loadBalancerSourceRanges: - description: 'If specified and supported by the platform, this will - restrict traffic through the cloud-provider load-balancer will be - restricted to the specified client IPs. This field will be ignored - if the cloud-provider does not support the feature." More info: - https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' - items: - type: string - type: array - ports: - description: 'The list of ports that are exposed by this service. - More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: The application protocol for this port. This field - follows standard Kubernetes label syntax. Un-prefixed names - are reserved for IANA standard service names (as per RFC-6335 - and https://www.iana.org/assignments/service-names). Non-standard - protocols should use prefixed names such as mycompany.com/my-custom-protocol. + serviceSpec: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + properties: + allocateLoadBalancerNodePorts: + description: allocateLoadBalancerNodePorts defines if NodePorts + will be automatically allocated for services with type LoadBalancer. Default + is "true". It may be set to "false" if the cluster load-balancer + does not rely on NodePorts. If the caller requests specific + NodePorts (by specifying a value), those requests will be respected, + regardless of this field. This field may only be set for services + with type LoadBalancer and will be cleared if the type is changed + to any other type. + type: boolean + clusterIP: + description: 'clusterIP is the IP address of the service and is + usually assigned randomly. If an address is specified manually, + is in-range (as per system configuration), and is not in use, + it will be allocated to the service; otherwise creation of the + service will fail. This field may not be changed through updates + unless the type field is also being changed to ExternalName + (which requires this field to be blank) or the type field is + being changed from ExternalName (in which case this field may + optionally be specified, as describe above). Valid values are + "None", empty string (""), or a valid IP address. Setting this + to "None" makes a "headless service" (no virtual IP), which + is useful when direct endpoint connections are preferred and + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. If this field is specified when + creating a Service of type ExternalName, creation will fail. + This field will be wiped when updating a Service to type ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + clusterIPs: + description: "ClusterIPs is a list of IP addresses assigned to + this service, and are usually assigned randomly. If an address + is specified manually, is in-range (as per system configuration), + and is not in use, it will be allocated to the service; otherwise + creation of the service will fail. This field may not be changed + through updates unless the type field is also being changed + to ExternalName (which requires this field to be empty) or the + type field is being changed from ExternalName (in which case + this field may optionally be specified, as describe above). + \ Valid values are \"None\", empty string (\"\"), or a valid + IP address. Setting this to \"None\" makes a \"headless service\" + (no virtual IP), which is useful when direct endpoint connections + are preferred and proxying is not required. Only applies to + types ClusterIP, NodePort, and LoadBalancer. If this field is + specified when creating a Service of type ExternalName, creation + will fail. This field will be wiped when updating a Service + to type ExternalName. If this field is not specified, it will + be initialized from the clusterIP field. If this field is specified, + clients must ensure that clusterIPs[0] and clusterIP have the + same value. \n This field may hold a maximum of two entries + (dual-stack IPs, in either order). These IPs must correspond + to the values of the ipFamilies field. Both clusterIPs and ipFamilies + are governed by the ipFamilyPolicy field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies" + items: type: string - name: - description: The name of this port within the service. This - must be a DNS_LABEL. All ports within a ServiceSpec must have - unique names. When considering the endpoints for a Service, - this must match the 'name' field in the EndpointPort. Optional - if only one ServicePort is defined on this service. + type: array + x-kubernetes-list-type: atomic + externalIPs: + description: externalIPs is a list of IP addresses for which nodes + in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: type: string - nodePort: - description: 'The port on each node on which this service is - exposed when type is NodePort or LoadBalancer. Usually assigned - by the system. If a value is specified, in-range, and not - in use it will be used, otherwise the operation will fail. If - not specified, a port will be allocated if this Service requires - one. If this field is specified when creating a Service which - does not need it, creation will fail. This field will be wiped - when updating a Service to no longer need it (e.g. changing - type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: The IP protocol for this port. Supports "TCP", - "UDP", and "SCTP". Default is TCP. + type: array + externalName: + description: externalName is the external reference that discovery + mechanisms will return as an alias for this service (e.g. a + DNS CNAME record). No proxying will be involved. Must be a + lowercase RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) + and requires `type` to be "ExternalName". + type: string + externalTrafficPolicy: + description: externalTrafficPolicy describes how nodes distribute + service traffic they receive on one of the Service's "externally-facing" + addresses (NodePorts, ExternalIPs, and LoadBalancer IPs). If + set to "Local", the proxy will configure the service in a way + that assumes that external load balancers will take care of + balancing the service traffic between nodes, and so each node + will deliver traffic only to the node-local endpoints of the + service, without masquerading the client source IP. (Traffic + mistakenly sent to a node with no endpoints will be dropped.) + The default value, "Cluster", uses the standard behavior of + routing to all endpoints evenly (possibly modified by topology + and other features). Note that traffic sent to an External IP + or LoadBalancer IP from within the cluster will always get "Cluster" + semantics, but clients sending to a NodePort from within the + cluster may need to take traffic policy into account when picking + a node. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. This only applies when type is set to LoadBalancer + and externalTrafficPolicy is set to Local. If a value is specified, + is in-range, and is not in use, it will be used. If not specified, + a value will be automatically allocated. External systems (e.g. + load-balancers) can use this port to determine if a given node + holds endpoints for this service or not. If this field is specified + when creating a Service which does not need it, creation will + fail. This field will be wiped when updating a Service to no + longer need it (e.g. changing type). This field cannot be updated + once set. + format: int32 + type: integer + internalTrafficPolicy: + description: InternalTrafficPolicy describes how nodes distribute + service traffic they receive on the ClusterIP. If set to "Local", + the proxy will assume that pods only want to talk to endpoints + of the service on the same node as the pod, dropping the traffic + if there are no local endpoints. The default value, "Cluster", + uses the standard behavior of routing to all endpoints evenly + (possibly modified by topology and other features). + type: string + ipFamilies: + description: "IPFamilies is a list of IP families (e.g. IPv4, + IPv6) assigned to this service. This field is usually assigned + automatically based on cluster configuration and the ipFamilyPolicy + field. If this field is specified manually, the requested family + is available in the cluster, and ipFamilyPolicy allows it, it + will be used; otherwise creation of the service will fail. This + field is conditionally mutable: it allows for adding or removing + a secondary IP family, but it does not allow changing the primary + IP family of the Service. Valid values are \"IPv4\" and \"IPv6\". + \ This field only applies to Services of types ClusterIP, NodePort, + and LoadBalancer, and does apply to \"headless\" services. This + field will be wiped when updating a Service to type ExternalName. + \n This field may hold a maximum of two entries (dual-stack + families, in either order). These families must correspond + to the values of the clusterIPs field, if specified. Both clusterIPs + and ipFamilies are governed by the ipFamilyPolicy field." + items: + description: IPFamily represents the IP Family (IPv4 or IPv6). + This type is used to express the family of an IP expressed + by a type (e.g. service.spec.ipFamilies). type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Number or name of the port to access on the pods - targeted by the service. Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. If this is a string, - it will be looked up as a named port in the target Pod''s - container ports. If this is not specified, the value of the - ''port'' field is used (an identity map). This field is ignored - for services with clusterIP=None, and should be omitted or - set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - description: publishNotReadyAddresses indicates that any agent which - deals with endpoints for this Service should disregard any indications - of ready/not-ready. The primary use case for setting this field - is for a StatefulSet's Headless Service to propagate SRV DNS records - for its Pods for the purpose of peer discovery. The Kubernetes controllers - that generate Endpoints and EndpointSlice resources for Services - interpret this to mean that all endpoints are considered "ready" - even if the Pods themselves are not. Agents which consume only Kubernetes - generated endpoints through the Endpoints or EndpointSlice resources - can safely assume this behavior. - type: boolean - selector: - additionalProperties: - type: string - description: 'Route service traffic to pods with label keys and values - matching this selector. If empty or not present, the service is - assumed to have an external process managing its endpoints, which - Kubernetes will not modify. Only applies to types ClusterIP, NodePort, - and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' - type: object - x-kubernetes-map-type: atomic - serviceAccountName: - type: string - sessionAffinity: - description: 'Supports "ClientIP" and "None". Used to maintain session - affinity. Enable client IP based session affinity. Must be ClientIP - or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' - type: string - sessionAffinityConfig: - description: sessionAffinityConfig contains the configurations of - session affinity. - properties: - clientIP: - description: clientIP contains the configurations of Client IP - based session affinity. + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + description: IPFamilyPolicy represents the dual-stack-ness requested + or required by this Service. If there is no value provided, + then this field will be set to SingleStack. Services can be + "SingleStack" (a single IP family), "PreferDualStack" (two IP + families on dual-stack configured clusters or a single IP family + on single-stack clusters), or "RequireDualStack" (two IP families + on dual-stack configured clusters, otherwise fail). The ipFamilies + and clusterIPs fields depend on the value of this field. This + field will be wiped when updating a service to type ExternalName. + type: string + loadBalancerClass: + description: loadBalancerClass is the class of the load balancer + implementation this Service belongs to. If specified, the value + of this field must be a label-style identifier, with an optional + prefix, e.g. "internal-vip" or "example.com/internal-vip". Unprefixed + names are reserved for end-users. This field can only be set + when the Service type is 'LoadBalancer'. If not set, the default + load balancer implementation is used, today this is typically + done through the cloud provider integration, but should apply + for any default implementation. If set, it is assumed that a + load balancer implementation is watching for Services with a + matching class. Any default load balancer implementation (e.g. + cloud providers) should ignore Services that set this field. + This field can only be set when creating or updating a Service + to type 'LoadBalancer'. Once set, it can not be changed. This + field will be wiped when a service is updated to a non 'LoadBalancer' + type. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not support + the feature. Deprecated: This field was under-specified and + its meaning varies across implementations, and it cannot support + dual-stack. As of Kubernetes v1.24, users are encouraged to + use implementation-specific annotations when available. This + field may be removed in a future API version.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field will + be ignored if the cloud-provider does not support the feature." + More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This + field follows standard Kubernetes label syntax. Un-prefixed + names are reserved for IANA standard service names (as + per RFC-6335 and https://www.iana.org/assignments/service-names). + Non-standard protocols should use prefixed names such + as mycompany.com/my-custom-protocol. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must + have unique names. When considering the endpoints for + a Service, this must match the 'name' field in the EndpointPort. + Optional if only one ServicePort is defined on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type is NodePort or LoadBalancer. Usually + assigned by the system. If a value is specified, in-range, + and not in use it will be used, otherwise the operation + will fail. If not specified, a port will be allocated + if this Service requires one. If this field is specified + when creating a Service which does not need it, creation + will fail. This field will be wiped when updating a Service + to no longer need it (e.g. changing type from NodePort + to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on the + pods targeted by the service. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. If this is + a string, it will be looked up as a named port in the + target Pod''s container ports. If this is not specified, + the value of the ''port'' field is used (an identity map). + This field is ignored for services with clusterIP=None, + and should be omitted or set equal to the ''port'' field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + description: publishNotReadyAddresses indicates that any agent + which deals with endpoints for this Service should disregard + any indications of ready/not-ready. The primary use case for + setting this field is for a StatefulSet's Headless Service to + propagate SRV DNS records for its Pods for the purpose of peer + discovery. The Kubernetes controllers that generate Endpoints + and EndpointSlice resources for Services interpret this to mean + that all endpoints are considered "ready" even if the Pods themselves + are not. Agents which consume only Kubernetes generated endpoints + through the Endpoints or EndpointSlice resources can safely + assume this behavior. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys and + values matching this selector. If empty or not present, the + service is assumed to have an external process managing its + endpoints, which Kubernetes will not modify. Only applies to + types ClusterIP, NodePort, and LoadBalancer. Ignored if type + is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. Must + be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. properties: - timeoutSeconds: - description: timeoutSeconds specifies the seconds of ClientIP - type session sticky time. The value must be >0 && <=86400(for - 1 day) if ServiceAffinity == "ClientIP". Default value is - 10800(for 3 hours). - format: int32 - type: integer + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of ClientIP + type session sticky time. The value must be >0 && <=86400(for + 1 day) if ServiceAffinity == "ClientIP". Default value + is 10800(for 3 hours). + format: int32 + type: integer + type: object type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ClusterIP" allocates a cluster-internal IP + address for load-balancing to endpoints. Endpoints are determined + by the selector or if that is not specified, by manual construction + of an Endpoints object or EndpointSlice objects. If clusterIP + is "None", no virtual IP is allocated and the endpoints are + published as a set of endpoints rather than a virtual IP. "NodePort" + builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the same endpoints + as the clusterIP. "ExternalName" aliases this service to the + specified externalName. Several other fields do not apply to + ExternalName services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string type: object - type: - description: 'type determines how the Service is exposed. Defaults - to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, - and LoadBalancer. "ClusterIP" allocates a cluster-internal IP address - for load-balancing to endpoints. Endpoints are determined by the - selector or if that is not specified, by manual construction of - an Endpoints object or EndpointSlice objects. If clusterIP is "None", - no virtual IP is allocated and the endpoints are published as a - set of endpoints rather than a virtual IP. "NodePort" builds on - ClusterIP and allocates a port on every node which routes to the - same endpoints as the clusterIP. "LoadBalancer" builds on NodePort - and creates an external load-balancer (if supported in the current - cloud) which routes to the same endpoints as the clusterIP. "ExternalName" - aliases this service to the specified externalName. Several other - fields do not apply to ExternalName services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' - type: string required: - flagSourceConfiguration type: object diff --git a/controllers/flagservice_controller.go b/controllers/flagservice_controller.go index 00b9c8452..4e863b385 100644 --- a/controllers/flagservice_controller.go +++ b/controllers/flagservice_controller.go @@ -36,10 +36,12 @@ import ( ) const ( - flagServiceCRDName = "FlagService" - flagServicePortName = "flag-service-port" - flagServicePort int32 = 80 - clusterRoleBindingName string = "open-feature-operator-flagd-kubernetes-sync" + flagServiceCRDName string = "FlagService" + flagServicePortName string = "flag-service-port" + flagServicePort int32 = 80 + flagProviderContainerName string = "flag-provider" + flagSelectorLabel string = "flag-provider" + clusterRoleBindingName string = "open-feature-operator-flagd-kubernetes-sync" ) // FlagServiceReconciler reconciles a FlagService object @@ -139,7 +141,7 @@ func (r *FlagServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) svc.OwnerReferences = flagSvcOwnerReferences svc.Namespace = constant.Namespace svc.Spec.Selector = map[string]string{ - "app": flagSvc.Name, + flagSelectorLabel: flagSvc.Name, } svc.Labels = flagSvc.Labels svc.Spec.Type = corev1.ServiceTypeClusterIP @@ -177,49 +179,53 @@ func (r *FlagServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) } else { deployment.Name = flagSvc.Name deployment.Namespace = constant.Namespace + //deployment.Spec = flagSvc.Spec.DeploymentSpec } } else { - // TODO: delete deployment deployment.Name = flagSvc.Name deployment.Namespace = constant.Namespace + //deployment.Spec = flagSvc.Spec.DeploymentSpec } - flagdContainer := corev1.Container{ - Name: "flagd", - Image: fmt.Sprintf("%s:%s", fsConfigSpec.Image, fsConfigSpec.Tag), - Args: []string{ - "start", - }, - ImagePullPolicy: corev1.PullAlways, // TODO: configurable - VolumeMounts: []corev1.VolumeMount{}, - Env: fsConfigSpec.EnvVars, - Ports: []corev1.ContainerPort{ - { - Name: "metrics", - ContainerPort: fsConfigSpec.MetricsPort, - }, + container := flagProviderContainer(deployment) + container.Name = flagProviderContainerName + container.Image = fmt.Sprintf("%s:%s", fsConfigSpec.Image, fsConfigSpec.Tag) + container.Args = []string{"start"} + container.ImagePullPolicy = corev1.PullAlways // TODO: configurable + container.VolumeMounts = []corev1.VolumeMount{} + container.Env = fsConfigSpec.EnvVars + container.Ports = []corev1.ContainerPort{ + { + Name: "metrics", + ContainerPort: fsConfigSpec.MetricsPort, }, - SecurityContext: nil, // TODO - // TODO resource limits } + container.SecurityContext = nil // TODO deployment.Spec.Template.ObjectMeta.Annotations = make(map[string]string) if err := HandleSourcesProviders(ctx, r.Log, r.Client, fsConfigSpec, fsConfigNs, constant.Namespace, flagSvc.Spec.ServiceAccountName, - flagSvcOwnerReferences, &deployment.Spec.Template.Spec, deployment.Spec.Template.ObjectMeta, &flagdContainer, + flagSvcOwnerReferences, &deployment.Spec.Template.Spec, deployment.Spec.Template.ObjectMeta, &container, ); err != nil { r.Log.Error(err, "handle source providers") return r.finishReconcile(nil, false) } + mergeFlagProviderContainer(deployment, container) + deployment.Spec.Template.Spec.ServiceAccountName = flagSvc.Spec.ServiceAccountName - labels := map[string]string{ - "app": flagSvc.Name, - } deployment.OwnerReferences = flagSvcOwnerReferences - deployment.Labels = labels - deployment.Spec.Template.Labels = labels - deployment.Spec.Selector = &metav1.LabelSelector{MatchLabels: labels} - deployment.Spec.Template.Spec.Containers = []corev1.Container{flagdContainer} + if deployment.Labels == nil { + deployment.Labels = make(map[string]string) + } + deployment.Labels[flagSelectorLabel] = flagSvc.Name + if deployment.Spec.Template.Labels == nil { + deployment.Spec.Template.Labels = make(map[string]string) + } + deployment.Spec.Template.Labels[flagSelectorLabel] = flagSvc.Name + if deployment.Spec.Selector == nil || deployment.Spec.Selector.MatchLabels == nil { + deployment.Spec.Selector = &metav1.LabelSelector{MatchLabels: make(map[string]string)} + } + deployment.Spec.Selector.MatchLabels[flagSelectorLabel] = flagSvc.Name if err := r.Client.Create(ctx, deployment); err != nil { r.Log.Error(err, "Failed to create deployment") @@ -261,3 +267,30 @@ func mergePorts(ports []corev1.ServicePort, port corev1.ServicePort) []corev1.Se return append(ports, port) } + +func flagProviderContainer(deployment *appsV1.Deployment) corev1.Container { + for _, container := range deployment.Spec.Template.Spec.Containers { + if container.Name == flagProviderContainerName { + return container + } + } + + return corev1.Container{} +} + +func mergeFlagProviderContainer(deployment *appsV1.Deployment, flagProviderContainer corev1.Container) { + if len(deployment.Spec.Template.Spec.Containers) == 0 { + deployment.Spec.Template.Spec.Containers = []corev1.Container{flagProviderContainer} + return + } + + for i := 0; i < len(deployment.Spec.Template.Spec.Containers); i++ { + existingFlagProviderContainer := deployment.Spec.Template.Spec.Containers[i] + if existingFlagProviderContainer.Name == flagProviderContainerName { + deployment.Spec.Template.Spec.Containers[i] = flagProviderContainer + return + } + } + + deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, flagProviderContainer) +} diff --git a/controllers/provider.go b/controllers/provider.go index edf769b31..a72d2074a 100644 --- a/controllers/provider.go +++ b/controllers/provider.go @@ -13,9 +13,9 @@ import ( ) const ( - rootFileSyncMountPath string = "/etc/flagd" - AllowKubernetesSyncAnnotation = "allowkubernetessync" - OpenFeatureAnnotationPrefix = "openfeature.dev" + rootFileSyncMountPath = "/etc/flagd" + AllowKubernetesSyncAnnotation = "allowkubernetessync" + OpenFeatureAnnotationPrefix = "openfeature.dev" ) func HandleSourcesProviders( diff --git a/webhooks/pod_webhook.go b/webhooks/pod_webhook.go index bafcfc6cb..071ebcc32 100644 --- a/webhooks/pod_webhook.go +++ b/webhooks/pod_webhook.go @@ -27,14 +27,13 @@ import ( const ( FlagDImagePullPolicy corev1.PullPolicy = "Always" clusterRoleBindingName string = "open-feature-operator-flagd-kubernetes-sync" - rootFileSyncMountPath string = "/etc/flagd" - OpenFeatureAnnotationPath = "metadata.annotations.openfeature.dev/openfeature.dev" - FlagSourceConfigurationAnnotation = "flagsourceconfiguration" - FeatureFlagConfigurationAnnotation = "featureflagconfiguration" - EnabledAnnotation = "enabled" - ProbeReadiness = "/readyz" - ProbeLiveness = "/healthz" - ProbeInitialDelay = 5 + OpenFeatureAnnotationPath string = "metadata.annotations.openfeature.dev/openfeature.dev" + FlagSourceConfigurationAnnotation string = "flagsourceconfiguration" + FeatureFlagConfigurationAnnotation string = "featureflagconfiguration" + EnabledAnnotation string = "enabled" + ProbeReadiness string = "/readyz" + ProbeLiveness string = "/healthz" + ProbeInitialDelay int32 = 5 ) // NOTE: RBAC not needed here.