Repository: open-telemetry/opentelemetry-operator
Pull request #3848 (2025-03-25 00:21:09)

Update CRD/Manifest/Controller/etc to be able to specify CollectorNotReadyGracePeriod
**Description:**
This is a follow-up PR for the issue of [#3781].

The current progress is we've merged in a new config `CollectorNotReadyGracePeriod` on TargetAllocator config, see PR [#3807].

In order to configure `CollectorNotReadyGracePeriod` with TA/OTEL CRDs, this follow-up PR is required.

**Link to tracking Issue(s):** 

- Resolves: #3781

**Testing:** 

(Tested without TargetAllocator CRD, IMO should be fine, just need to make sure `CollectorNotReadyGracePeriod` is added when creating TargetAllocator CR, which is done in `internal/manifests/collector/targetallocator.go`)

1. Verified that the conversion from "string" to "time.Duration" is valid by unit test under `/cmd/otel-allocator/internal/config/config_test.go` and by integration test in my local.

Note that without the change under `/cmd/otel-allocator/internal/config.go`, I used to get 
```
~ % kubectl logs -n monitoring otel-collector-with-ta-vm-proxy-targetallocator-7674c57b4cn8xst
Failed to load config: decoding failed due to the following error(s):

'collector_not_ready_grace_period' expected type 'time.Duration', got unconvertible type 'string', value: '30s'%
``` 

2. Verified that "collector_not_ready_grace_period" on TA configmap will default to be `0s` if nothing is provided in TA/OTEL CRDs. See details under `/internal/manifests/targetallocator/configmap_test.go`

3. Verified the 30s grace period works by manually making collector pods to be unhealthy.

```
otel-collector-with-ta-vm-proxy-collector-0                       0/1     ErrImagePull       0          41s
otel-collector-with-ta-vm-proxy-targetallocator-b757cb6d6-pmfwb   1/1     Running            0          5m1s
otel-collector-with-ta-vm-proxy-targetallocator-b757cb6d6-t7rpn   1/1     Running            0          5m6s
```

Before:

![image](https://github.com/user-attachments/assets/4fe929ac-365b-40d6-97c6-2f873aee067a)

After:

![image](https://github.com/user-attachments/assets/aa2a1530-6e11-4f51-ae1a-9933ff600445)


rice-junhaoyu: Hey @swiatekm @jaronoff97

Spent quite a while to understand how the CRD/Controller/Manifest/etc stuffs work.

Can I get a few early comments? Just wanted to know if I am on the right track or not. I will fix the failing checks in next revision so no worries.
rice-junhaoyu: I am pretty sure that I am messed up by the `/bundle` directory that failing the unit tests... Will find some time tomorrow to see how it works.
rice-junhaoyu: @swiatekm Hey Mikolaj, hope your KubeCon journey was successful and epic!!

May I get a code review on this PR when you are available? Thank you!!
rice-junhaoyu: I actually succeeded `--- FAIL: TestNamespaceLabelUpdate (0.52s)` in local...

Give me some time to debug it.
swiatekm: > TestNamespaceLabelUpdate

This is a test that's flaky on main, for the record.

Patch:
diff --git a/.chloggen/crd_changes_for_collector_grace_period.yaml b/.chloggen/crd_changes_for_collector_grace_period.yaml
new file mode 100644
index 00000000..9944046c
--- /dev/null
+++ b/.chloggen/crd_changes_for_collector_grace_period.yaml
@@ -0,0 +1,16 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: enhancement
+
+# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
+component: target allocator
+
+# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: Specific changes on CRD, Manifest, Controller, etc to allow specifying value for collector grace period
+
+# One or more tracking issues related to the change
+issues: [3781]
+
+# (Optional) One or more lines of additional information to render under the primary note.
+# These lines will be padded with 2 spaces and then inserted directly into the document.
+# Use pipe (|) for multiline entries.
+subtext:
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9114a5b7..e3ba6fc2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -159,7 +159,7 @@ Before starting the development of a new feature, please create an issue and dis
 
 ### Bug fixes
 
-Every bug fix should be accompanied with a unit test, so that we can prevent regressions.
+Every bug fix should be accompanied by a unit test, so that we can prevent regressions.
 
 ### Documentation, typos, ...
 
diff --git a/Makefile b/Makefile
index 1f7b0440..f6326629 100644
--- a/Makefile
+++ b/Makefile
@@ -113,7 +113,7 @@ MANIFEST_DIR ?= config/crd/bases
 # kubectl apply does not work on large CRDs.
 CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true,maxDescLen=0"
 
-# Choose wich version to generate
+# Choose which version to generate
 BUNDLE_VARIANT ?= community
 BUNDLE_DIR = ./bundle/$(BUNDLE_VARIANT)
 MANIFESTS_DIR = config/manifests/$(BUNDLE_VARIANT)
diff --git a/apis/v1alpha1/ingress_type.go b/apis/v1alpha1/ingress_type.go
index 6448cbfd..d002a11a 100644
--- a/apis/v1alpha1/ingress_type.go
+++ b/apis/v1alpha1/ingress_type.go
@@ -12,7 +12,7 @@ type (
 const (
 	// IngressTypeNginx specifies that an ingress entry should be created.
 	IngressTypeNginx IngressType = "ingress"
-	// IngressTypeOpenshiftRoute specifies that an route entry should be created.
+	// IngressTypeRoute specifies that a route entry should be created.
 	IngressTypeRoute IngressType = "route"
 )
 
@@ -28,10 +28,10 @@ const (
 	// TLSRouteTerminationTypeEdge indicates that encryption should be terminated
 	// at the edge router.
 	TLSRouteTerminationTypeEdge TLSRouteTerminationType = "edge"
-	// TLSTerminationPassthrough indicates that the destination service is
+	// TLSRouteTerminationTypePassthrough indicates that the destination service is
 	// responsible for decrypting traffic.
 	TLSRouteTerminationTypePassthrough TLSRouteTerminationType = "passthrough"
-	// TLSTerminationReencrypt indicates that traffic will be decrypted on the edge
+	// TLSRouteTerminationTypeReencrypt indicates that traffic will be decrypted on the edge
 	// and re-encrypt using a new certificate.
 	TLSRouteTerminationTypeReencrypt TLSRouteTerminationType = "reencrypt"
 )
diff --git a/apis/v1alpha1/instrumentation_types.go b/apis/v1alpha1/instrumentation_types.go
index 96639f62..5caf3fb0 100644
--- a/apis/v1alpha1/instrumentation_types.go
+++ b/apis/v1alpha1/instrumentation_types.go
@@ -157,7 +157,7 @@ type Java struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -195,7 +195,7 @@ type NodeJS struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -220,7 +220,7 @@ type Python struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -245,7 +245,7 @@ type DotNet struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -268,7 +268,7 @@ type Go struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -293,7 +293,7 @@ type ApacheHttpd struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
@@ -333,7 +333,7 @@ type Nginx struct {
 	// +optional
 	Image string `json:"image,omitempty"`
 
-	// VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+	// VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 	// If omitted, an emptyDir is used with size limit VolumeSizeLimit
 	VolumeClaimTemplate corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplate,omitempty"`
 
diff --git a/apis/v1alpha1/opentelemetrycollector_types.go b/apis/v1alpha1/opentelemetrycollector_types.go
index f97a7f9d..4adebe5f 100644
--- a/apis/v1alpha1/opentelemetrycollector_types.go
+++ b/apis/v1alpha1/opentelemetrycollector_types.go
@@ -98,7 +98,7 @@ type OpenTelemetryCollectorSpec struct {
 	// Args is the set of arguments to pass to the OpenTelemetry Collector binary
 	// +optional
 	Args map[string]string `json:"args,omitempty"`
-	// Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling
+	// Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if you are not using autoscaling
 	// +optional
 	Replicas *int32 `json:"replicas,omitempty"`
 	// MinReplicas sets a lower bound to the autoscaling feature.  Set this if you are using autoscaling. It must be at least 1
@@ -474,7 +474,7 @@ type OpenTelemetryCollectorList struct {
 
 // AutoscalerSpec defines the OpenTelemetryCollector's pod autoscaling specification.
 type AutoscalerSpec struct {
-	// MinReplicas sets a lower bound to the autoscaling feature.  Set this if your are using autoscaling. It must be at least 1
+	// MinReplicas sets a lower bound to the autoscaling feature.  Set this if you are using autoscaling. It must be at least 1
 	// +optional
 	MinReplicas *int32 `json:"minReplicas,omitempty"`
 	// MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
diff --git a/apis/v1alpha1/targetallocator_types.go b/apis/v1alpha1/targetallocator_types.go
index ea5ddfff..aaf11a54 100644
--- a/apis/v1alpha1/targetallocator_types.go
+++ b/apis/v1alpha1/targetallocator_types.go
@@ -86,4 +86,9 @@ type TargetAllocatorSpec struct {
 	// +kubebuilder:validation:Optional
 	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Observability"
 	Observability v1beta1.ObservabilitySpec `json:"observability,omitempty"`
+	// CollectorNotReadyGracePeriod defines the grace period after which a TargetAllocator stops considering a collector is target assignable.
+	// The default is 0s, which means that all collectors can be assigned targets irrespective of their readiness.
+	//
+	// +optional
+	CollectorNotReadyGracePeriod *metav1.Duration `json:"collectorNotReadyGracePeriod,omitempty"`
 }
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index 0caa0157..343eb9da 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -1388,6 +1388,11 @@ func (in *TargetAllocatorSpec) DeepCopyInto(out *TargetAllocatorSpec) {
 	}
 	in.PrometheusCR.DeepCopyInto(&out.PrometheusCR)
 	out.Observability = in.Observability
+	if in.CollectorNotReadyGracePeriod != nil {
+		in, out := &in.CollectorNotReadyGracePeriod, &out.CollectorNotReadyGracePeriod
+		*out = new(metav1.Duration)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorSpec.
diff --git a/apis/v1beta1/common.go b/apis/v1beta1/common.go
index 23500850..fd6ed27a 100644
--- a/apis/v1beta1/common.go
+++ b/apis/v1beta1/common.go
@@ -35,7 +35,7 @@ type MetricSpec struct {
 
 // AutoscalerSpec defines the OpenTelemetryCollector's pod autoscaling specification.
 type AutoscalerSpec struct {
-	// MinReplicas sets a lower bound to the autoscaling feature.  Set this if your are using autoscaling. It must be at least 1
+	// MinReplicas sets a lower bound to the autoscaling feature.  Set this if you are using autoscaling. It must be at least 1
 	// +optional
 	MinReplicas *int32 `json:"minReplicas,omitempty"`
 	// MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
diff --git a/apis/v1beta1/ingress.go b/apis/v1beta1/ingress.go
index 1e9d5f4a..3d9dc5fa 100644
--- a/apis/v1beta1/ingress.go
+++ b/apis/v1beta1/ingress.go
@@ -14,7 +14,7 @@ type (
 const (
 	// IngressTypeIngress specifies that an ingress should be created.
 	IngressTypeIngress IngressType = "ingress"
-	// IngressTypeRoute IngressTypeOpenshiftRoute specifies that an route should be created.
+	// IngressTypeRoute IngressTypeOpenshiftRoute specifies that a route should be created.
 	IngressTypeRoute IngressType = "route"
 )
 
@@ -30,10 +30,10 @@ const (
 	// TLSRouteTerminationTypeEdge indicates that encryption should be terminated
 	// at the edge router.
 	TLSRouteTerminationTypeEdge TLSRouteTerminationType = "edge"
-	// TLSTerminationPassthrough indicates that the destination service is
+	// TLSRouteTerminationTypePassthrough indicates that the destination service is
 	// responsible for decrypting traffic.
 	TLSRouteTerminationTypePassthrough TLSRouteTerminationType = "passthrough"
-	// TLSTerminationReencrypt indicates that traffic will be decrypted on the edge
+	// TLSRouteTerminationTypeReencrypt indicates that traffic will be decrypted on the edge
 	// and re-encrypt using a new certificate.
 	TLSRouteTerminationTypeReencrypt TLSRouteTerminationType = "reencrypt"
 )
diff --git a/apis/v1beta1/opentelemetrycollector_types.go b/apis/v1beta1/opentelemetrycollector_types.go
index 1510a04a..f788faee 100644
--- a/apis/v1beta1/opentelemetrycollector_types.go
+++ b/apis/v1beta1/opentelemetrycollector_types.go
@@ -210,6 +210,11 @@ type TargetAllocatorEmbedded struct {
 	//
 	// +optional
 	PodDisruptionBudget *PodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"`
+	// CollectorNotReadyGracePeriod defines the grace period after which a TargetAllocator stops considering a collector is target assignable.
+	// The default is 0s, which means that all collectors can be assigned targets irrespective of their readiness.
+	//
+	// +optional
+	CollectorNotReadyGracePeriod *metav1.Duration `json:"collectorNotReadyGracePeriod,omitempty"`
 }
 
 // Probe defines the OpenTelemetry's pod probe config.
diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go
index da29269f..8b01b5c8 100644
--- a/apis/v1beta1/zz_generated.deepcopy.go
+++ b/apis/v1beta1/zz_generated.deepcopy.go
@@ -746,6 +746,11 @@ func (in *TargetAllocatorEmbedded) DeepCopyInto(out *TargetAllocatorEmbedded) {
 		*out = new(PodDisruptionBudgetSpec)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.CollectorNotReadyGracePeriod != nil {
+		in, out := &in.CollectorNotReadyGracePeriod, &out.CollectorNotReadyGracePeriod
+		*out = new(metav1.Duration)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetAllocatorEmbedded.
diff --git a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
index b3a67ffe..567f5892 100644
--- a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
+++ b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml
@@ -99,7 +99,7 @@ metadata:
     categories: Logging & Tracing,Monitoring
     certified: "false"
     containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
-    createdAt: "2025-03-31T16:55:47Z"
+    createdAt: "2025-04-07T17:06:33Z"
     description: Provides the OpenTelemetry components, including the Collector
     operators.operatorframework.io/builder: operator-sdk-v1.29.0
     operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
diff --git a/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml b/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml
index 1ce98fc9..8ce05e81 100644
--- a/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml
+++ b/bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml
@@ -7705,6 +7705,8 @@ spec:
                     - consistent-hashing
                     - per-node
                     type: string
+                  collectorNotReadyGracePeriod:
+                    type: string
                   enabled:
                     type: boolean
                   env:
diff --git a/bundle/community/manifests/opentelemetry.io_targetallocators.yaml b/bundle/community/manifests/opentelemetry.io_targetallocators.yaml
index 55f539a8..5e1da99a 100644
--- a/bundle/community/manifests/opentelemetry.io_targetallocators.yaml
+++ b/bundle/community/manifests/opentelemetry.io_targetallocators.yaml
@@ -1170,6 +1170,8 @@ spec:
                 additionalProperties:
                   type: string
                 type: object
+              collectorNotReadyGracePeriod:
+                type: string
               env:
                 items:
                   properties:
diff --git a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
index cee07847..9c05d43b 100644
--- a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
+++ b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml
@@ -99,7 +99,7 @@ metadata:
     categories: Logging & Tracing,Monitoring
     certified: "false"
     containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
-    createdAt: "2025-03-31T16:55:47Z"
+    createdAt: "2025-04-07T17:06:33Z"
     description: Provides the OpenTelemetry components, including the Collector
     operators.operatorframework.io/builder: operator-sdk-v1.29.0
     operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
diff --git a/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml b/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml
index 1ce98fc9..8ce05e81 100644
--- a/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml
+++ b/bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml
@@ -7705,6 +7705,8 @@ spec:
                     - consistent-hashing
                     - per-node
                     type: string
+                  collectorNotReadyGracePeriod:
+                    type: string
                   enabled:
                     type: boolean
                   env:
diff --git a/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml b/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml
index 55f539a8..5e1da99a 100644
--- a/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml
+++ b/bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml
@@ -1170,6 +1170,8 @@ spec:
                 additionalProperties:
                   type: string
                 type: object
+              collectorNotReadyGracePeriod:
+                type: string
               env:
                 items:
                   properties:
diff --git a/cmd/operator-opamp-bridge/agent/agent.go b/cmd/operator-opamp-bridge/agent/agent.go
index b566ede2..488e3af8 100644
--- a/cmd/operator-opamp-bridge/agent/agent.go
+++ b/cmd/operator-opamp-bridge/agent/agent.go
@@ -141,7 +141,7 @@ func (agent *Agent) generateCollectorPoolHealth() (map[string]*protobufs.Compone
 	return healthMap, nil
 }
 
-// getCollectorSelector destructures the collectors scale selector if present, if uses the labelmap from the operator.
+// getCollectorSelector destructures the collectors scale selector if present, it uses the labelmap from the operator.
 func (agent *Agent) getCollectorSelector(col v1beta1.OpenTelemetryCollector) map[string]string {
 	if len(col.Status.Scale.Selector) > 0 {
 		selMap := map[string]string{}
@@ -289,7 +289,7 @@ func (agent *Agent) runHeartbeat() {
 	}
 }
 
-// updateAgentIdentity receives a new instanced Id from the remote server and updates the agent's instanceID field.
+// updateAgentIdentity receives a new instancedId from the remote server and updates the agent's instanceID field.
 // The meter will be reinitialized by the onMessage function.
 func (agent *Agent) updateAgentIdentity(instanceId uuid.UUID) {
 	agent.logger.V(3).Info("Agent identity is being changed",
diff --git a/cmd/operator-opamp-bridge/config/config.go b/cmd/operator-opamp-bridge/config/config.go
index d41bff82..6dad4517 100644
--- a/cmd/operator-opamp-bridge/config/config.go
+++ b/cmd/operator-opamp-bridge/config/config.go
@@ -204,7 +204,7 @@ func keyValuePair(key string, value string) *protobufs.KeyValue {
 func mustGetInstanceId() uuid.UUID {
 	u, err := uuid.NewV7()
 	if err != nil {
-		// This really should never happen and if it does we should fail.
+		// This really should never happen and if it does, we should fail.
 		panic(err)
 	}
 	return u
diff --git a/cmd/otel-allocator/internal/config/config.go b/cmd/otel-allocator/internal/config/config.go
index f6e38499..f118fc33 100644
--- a/cmd/otel-allocator/internal/config/config.go
+++ b/cmd/otel-allocator/internal/config/config.go
@@ -90,10 +90,10 @@ type HTTPSServerConfig struct {
 	TLSKeyFilePath  string `yaml:"tls_key_file_path,omitempty"`
 }
 
-// StringToModelDurationHookFunc returns a DecodeHookFuncType
-// that converts string to time.Duration, which can be used
+// StringToModelOrTimeDurationHookFunc returns a DecodeHookFuncType
+// that converts string to time.Duration, which can also be used
 // as model.Duration.
-func StringToModelDurationHookFunc() mapstructure.DecodeHookFuncType {
+func StringToModelOrTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
 	return func(
 		f reflect.Type,
 		t reflect.Type,
@@ -103,7 +103,7 @@ func StringToModelDurationHookFunc() mapstructure.DecodeHookFuncType {
 			return data, nil
 		}
 
-		if t != reflect.TypeOf(model.Duration(5)) {
+		if t != reflect.TypeOf(model.Duration(5)) && t != reflect.TypeOf(time.Duration(5)) {
 			return data, nil
 		}
 
@@ -316,7 +316,7 @@ func unmarshal(cfg *Config, configFile string) error {
 		TagName: "yaml",
 		Result:  cfg,
 		DecodeHook: mapstructure.ComposeDecodeHookFunc(
-			StringToModelDurationHookFunc(),
+			StringToModelOrTimeDurationHookFunc(),
 			MapToPromConfig(),
 			MapToLabelSelector(),
 		),
diff --git a/cmd/otel-allocator/internal/target/discovery.go b/cmd/otel-allocator/internal/target/discovery.go
index 1a35ffb8..f9bce0c2 100644
--- a/cmd/otel-allocator/internal/target/discovery.go
+++ b/cmd/otel-allocator/internal/target/discovery.go
@@ -101,7 +101,7 @@ func (m *Discoverer) ApplyConfig(source allocatorWatcher.EventSource, scrapeConf
 		return err
 	}
 	// If the hash has changed, updated stored hash and send the new config.
-	// Otherwise skip updating scrape configs.
+	// Otherwise, skip updating scrape configs.
 	if m.scrapeConfigsUpdater != nil && m.scrapeConfigsHash != hash {
 		err := m.scrapeConfigsUpdater.UpdateScrapeConfigResponse(jobToScrapeConfig)
 		if err != nil {
diff --git a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml
index 7f40cdd8..75ddf5cb 100644
--- a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml
+++ b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml
@@ -7691,6 +7691,8 @@ spec:
                     - consistent-hashing
                     - per-node
                     type: string
+                  collectorNotReadyGracePeriod:
+                    type: string
                   enabled:
                     type: boolean
                   env:
diff --git a/config/crd/bases/opentelemetry.io_targetallocators.yaml b/config/crd/bases/opentelemetry.io_targetallocators.yaml
index ce8432e9..86fccaa1 100644
--- a/config/crd/bases/opentelemetry.io_targetallocators.yaml
+++ b/config/crd/bases/opentelemetry.io_targetallocators.yaml
@@ -1167,6 +1167,8 @@ spec:
                 additionalProperties:
                   type: string
                 type: object
+              collectorNotReadyGracePeriod:
+                type: string
               env:
                 items:
                   properties:
diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml
index 4ac6ff22..f638c666 100644
--- a/config/default/manager_auth_proxy_patch.yaml
+++ b/config/default/manager_auth_proxy_patch.yaml
@@ -1,4 +1,4 @@
-# This patch inject a sidecar container which is a HTTP proxy for the 
+# This patch inject a sidecar container which is an HTTP proxy for the
 # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
 apiVersion: apps/v1
 kind: Deployment
diff --git a/docs/api/instrumentations.md b/docs/api/instrumentations.md
index 404c9630..87ac838c 100644
--- a/docs/api/instrumentations.md
+++ b/docs/api/instrumentations.md
@@ -261,7 +261,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecapachehttpdvolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -911,7 +911,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -1444,7 +1444,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecdotnetvolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -1824,7 +1824,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -2699,7 +2699,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecgovolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -3079,7 +3079,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -3589,7 +3589,7 @@ All extensions are copied to a single directory; if a JAR with the same name exi
         <td><b><a href="#instrumentationspecjavavolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -4003,7 +4003,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -4522,7 +4522,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecnginxvolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -5172,7 +5172,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -5674,7 +5674,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecnodejsvolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -6054,7 +6054,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
@@ -6556,7 +6556,7 @@ If the former var had been defined, then the other vars would be ignored.<br/>
         <td><b><a href="#instrumentationspecpythonvolumeclaimtemplate">volumeClaimTemplate</a></b></td>
         <td>object</td>
         <td>
-          VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+          VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit<br/>
         </td>
         <td>false</td>
@@ -6936,7 +6936,7 @@ only the result of this request.<br/>
 
 
 
-VolumeClaimTemplate defines a ephemeral volume used for auto-instrumentation.
+VolumeClaimTemplate defines an ephemeral volume used for auto-instrumentation.
 If omitted, an emptyDir is used with size limit VolumeSizeLimit
 
 <table>
diff --git a/docs/api/opentelemetrycollectors.md b/docs/api/opentelemetrycollectors.md
index f4a29a46..f2bd3203 100644
--- a/docs/api/opentelemetrycollectors.md
+++ b/docs/api/opentelemetrycollectors.md
@@ -325,7 +325,7 @@ default.<br/>
         <td><b>replicas</b></td>
         <td>integer</td>
         <td>
-          Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling<br/>
+          Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if you are not using autoscaling<br/>
           <br/>
             <i>Format</i>: int32<br/>
         </td>
@@ -5012,7 +5012,7 @@ Use TargetCPUUtilization or TargetMemoryUtilization instead if scaling on these
         <td><b>minReplicas</b></td>
         <td>integer</td>
         <td>
-          MinReplicas sets a lower bound to the autoscaling feature.  Set this if your are using autoscaling. It must be at least 1<br/>
+          MinReplicas sets a lower bound to the autoscaling feature.  Set this if you are using autoscaling. It must be at least 1<br/>
           <br/>
             <i>Format</i>: int32<br/>
         </td>
@@ -24534,7 +24534,7 @@ Use TargetCPUUtilization or TargetMemoryUtilization instead if scaling on these
         <td><b>minReplicas</b></td>
         <td>integer</td>
         <td>
-          MinReplicas sets a lower bound to the autoscaling feature.  Set this if your are using autoscaling. It must be at least 1<br/>
+          MinReplicas sets a lower bound to the autoscaling feature.  Set this if you are using autoscaling. It must be at least 1<br/>
           <br/>
             <i>Format</i>: int32<br/>
         </td>
@@ -30714,6 +30714,14 @@ WARNING: The per-node strategy currently ignores targets without a Node, like co
             <i>Default</i>: consistent-hashing<br/>
         </td>
         <td>false</td>
+      </tr><tr>
+        <td><b>collectorNotReadyGracePeriod</b></td>
+        <td>string</td>
+        <td>
+          CollectorNotReadyGracePeriod defines the grace period after which a TargetAllocator stops considering a collector is target assignable.
+The default is 0s, which means that all collectors can be assigned targets irrespective of their readiness.<br/>
+        </td>
+        <td>false</td>
       </tr><tr>
         <td><b>enabled</b></td>
         <td>boolean</td>
diff --git a/docs/api/targetallocators.md b/docs/api/targetallocators.md
index 99a7591e..b1706057 100644
--- a/docs/api/targetallocators.md
+++ b/docs/api/targetallocators.md
@@ -137,6 +137,14 @@ WARNING: The per-node strategy currently ignores targets without a Node, like co
           Args is the set of arguments to pass to the main container's binary.<br/>
         </td>
         <td>false</td>
+      </tr><tr>
+        <td><b>collectorNotReadyGracePeriod</b></td>
+        <td>string</td>
+        <td>
+          CollectorNotReadyGracePeriod defines the grace period after which a TargetAllocator stops considering a collector is target assignable.
+The default is 0s, which means that all collectors can be assigned targets irrespective of their readiness.<br/>
+        </td>
+        <td>false</td>
       </tr><tr>
         <td><b><a href="#targetallocatorspecenvindex">env</a></b></td>
         <td>[]object</td>
diff --git a/internal/components/receivers/k8sevents.go b/internal/components/receivers/k8sevents.go
index 89bbda4f..de2c0430 100644
--- a/internal/components/receivers/k8sevents.go
+++ b/internal/components/receivers/k8sevents.go
@@ -11,7 +11,7 @@ import (
 type k8seventsConfig struct{}
 
 func generatek8seventsRbacRules(_ logr.Logger, _ k8seventsConfig) ([]rbacv1.PolicyRule, error) {
-	// The k8s Events Receiver needs get permissions on the following resources always.
+	// The k8s Events Receiver needs the get permissions on the following resources always.
 	return []rbacv1.PolicyRule{
 		{
 			APIGroups: []string{""},
diff --git a/internal/components/receivers/kubeletstats.go b/internal/components/receivers/kubeletstats.go
index 715c10c3..84315845 100644
--- a/internal/components/receivers/kubeletstats.go
+++ b/internal/components/receivers/kubeletstats.go
@@ -42,7 +42,7 @@ func generateKubeletStatsEnvVars(_ logr.Logger, config kubeletStatsConfig) ([]co
 }
 
 func generateKubeletStatsRbacRules(_ logr.Logger, config kubeletStatsConfig) ([]rbacv1.PolicyRule, error) {
-	// The Kubelet Stats Receiver needs get permissions on the nodes/stats resources always.
+	// The Kubelet Stats Receiver needs the get permissions on the nodes/stats resources always.
 	prs := []rbacv1.PolicyRule{
 		{
 			APIGroups: []string{""},
@@ -52,7 +52,7 @@ func generateKubeletStatsRbacRules(_ logr.Logger, config kubeletStatsConfig) ([]
 	}
 
 	// Additionally, when using extra_metadata_labels or any of the {request|limit}_utilization metrics
-	// the processor also needs get permissions for nodes/proxy resources.
+	// the processor also needs the get permissions for nodes/proxy resources.
 	nodesProxyPr := rbacv1.PolicyRule{
 		APIGroups: []string{""},
 		Resources: []string{"nodes/proxy"},
diff --git a/internal/manifests/collector/ingress.go b/internal/manifests/collector/ingress.go
index 497e29c4..d598dc72 100644
--- a/internal/manifests/collector/ingress.go
+++ b/internal/manifests/collector/ingress.go
@@ -26,7 +26,7 @@ func Ingress(params manifests.Params) (*networkingv1.Ingress, error) {
 
 	ports, err := servicePortsFromCfg(params.Log, params.OtelCol)
 
-	// if we have no ports, we don't need a ingress entry
+	// if we have no ports, we don't need an ingress entry
 	if len(ports) == 0 || err != nil {
 		params.Log.V(1).Info(
 			"the instance's configuration didn't yield any ports to open, skipping ingress",
diff --git a/internal/manifests/collector/route.go b/internal/manifests/collector/route.go
index 98193484..d7ac6e8e 100644
--- a/internal/manifests/collector/route.go
+++ b/internal/manifests/collector/route.go
@@ -42,7 +42,7 @@ func Routes(params manifests.Params) ([]*routev1.Route, error) {
 
 	ports, err := servicePortsFromCfg(params.Log, params.OtelCol)
 
-	// if we have no ports, we don't need a ingress entry
+	// if we have no ports, we don't need an ingress entry
 	if len(ports) == 0 || err != nil {
 		params.Log.V(1).Info(
 			"the instance's configuration didn't yield any ports to open, skipping ingress",
diff --git a/internal/manifests/collector/targetallocator.go b/internal/manifests/collector/targetallocator.go
index e0ee5fd7..4c2f5767 100644
--- a/internal/manifests/collector/targetallocator.go
+++ b/internal/manifests/collector/targetallocator.go
@@ -42,10 +42,11 @@ func TargetAllocator(params manifests.Params) (*v1alpha1.TargetAllocator, error)
 				PodAnnotations:            params.OtelCol.Spec.PodAnnotations,
 				PodDisruptionBudget:       taSpec.PodDisruptionBudget,
 			},
-			AllocationStrategy: taSpec.AllocationStrategy,
-			FilterStrategy:     taSpec.FilterStrategy,
-			PrometheusCR:       taSpec.PrometheusCR,
-			Observability:      taSpec.Observability,
+			AllocationStrategy:           taSpec.AllocationStrategy,
+			FilterStrategy:               taSpec.FilterStrategy,
+			PrometheusCR:                 taSpec.PrometheusCR,
+			Observability:                taSpec.Observability,
+			CollectorNotReadyGracePeriod: taSpec.CollectorNotReadyGracePeriod,
 		},
 	}, nil
 }
diff --git a/internal/manifests/targetallocator/configmap.go b/internal/manifests/targetallocator/configmap.go
index ed980d73..7d496fea 100644
--- a/internal/manifests/targetallocator/configmap.go
+++ b/internal/manifests/targetallocator/configmap.go
@@ -123,6 +123,10 @@ func ConfigMap(params Params) (*corev1.ConfigMap, error) {
 		}
 	}
 
+	if taSpec.CollectorNotReadyGracePeriod.Size() > 0 {
+		taConfig["collector_not_ready_grace_period"] = taSpec.CollectorNotReadyGracePeriod.Duration
+	}
+
 	taConfigYAML, err := yaml.Marshal(taConfig)
 	if err != nil {
 		return &corev1.ConfigMap{}, err
diff --git a/pkg/instrumentation/golang.go b/pkg/instrumentation/golang.go
index 653e01d0..454796ff 100644
--- a/pkg/instrumentation/golang.go
+++ b/pkg/instrumentation/golang.go
@@ -26,7 +26,7 @@ func injectGoSDK(goSpec v1alpha1.Go, pod corev1.Pod, cfg config.Config, instSpec
 		return pod, fmt.Errorf("shared process namespace has been explicitly disabled")
 	}
 
-	// skip instrumentation when more than one containers provided
+	// skip instrumentation when more than one container is provided
 	containerNames := ""
 	ok := false
 	if cfg.EnableMultiInstrumentation() {
diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go
index 0dac6873..ee19a16d 100644
--- a/pkg/instrumentation/sdk.go
+++ b/pkg/instrumentation/sdk.go
@@ -230,7 +230,7 @@ func (i *sdkInjector) setInitContainerSecurityContext(pod corev1.Pod, securityCo
 
 func getContainerIndex(containerName string, pod corev1.Pod) int {
 	// We search for specific container to inject variables and if no one is found
-	// We fallback to first container
+	// We fall back to first container
 	var index = 0
 	for idx, ctnair := range pod.Spec.Containers {
 		if ctnair.Name == containerName {
@@ -279,7 +279,7 @@ func (i *sdkInjector) injectCommonEnvVar(otelinst v1alpha1.Instrumentation, pod
 
 // injectCommonSDKConfig adds common SDK configuration environment variables to the necessary pod
 // agentIndex represents the index of the pod the needs the env vars to instrument the application.
-// appIndex represents the index of the pod the will produce the telemetry.
+// appIndex represents the index of the pod that will produce the telemetry.
 // When the pod handling the instrumentation is the same as the pod producing the telemetry agentIndex
 // and appIndex should be the same value.  This is true for dotnet, java, nodejs, and python instrumentations.
 // Go requires the agent to be a different container in the pod, so the agentIndex should represent this new sidecar
diff --git a/pkg/sidecar/annotation.go b/pkg/sidecar/annotation.go
index 51599402..90d1fa48 100644
--- a/pkg/sidecar/annotation.go
+++ b/pkg/sidecar/annotation.go
@@ -26,7 +26,7 @@ func annotationValue(ns corev1.Namespace, pod corev1.Pod) string {
 		return podAnnValue
 	}
 
-	// if the pod value is empty, the annotation annotation should be used (true, false, instance)
+	// if the pod value is empty, the namespace annotation should be used (true, false, instance)
 	if len(podAnnValue) == 0 {
 		return nsAnnValue
 	}


Test patch:
diff --git a/apis/v1alpha1/opampbridge_webhook_test.go b/apis/v1alpha1/opampbridge_webhook_test.go
index f3fea67c..05097f75 100644
--- a/apis/v1alpha1/opampbridge_webhook_test.go
+++ b/apis/v1alpha1/opampbridge_webhook_test.go
@@ -211,7 +211,7 @@ func TestOpAMPBridgeValidatingWebhook(t *testing.T) {
 					},
 					Ports: []v1.ServicePort{
 						{
-							// this port name contains a non alphanumeric character, which is invalid.
+							// this port name contains a non-alphanumeric character, which is invalid.
 							Name:     "-test🦄port",
 							Port:     12345,
 							Protocol: v1.ProtocolTCP,
diff --git a/apis/v1alpha1/targetallocator_webhook_test.go b/apis/v1alpha1/targetallocator_webhook_test.go
index dbdfd2e2..7cbd83f5 100644
--- a/apis/v1alpha1/targetallocator_webhook_test.go
+++ b/apis/v1alpha1/targetallocator_webhook_test.go
@@ -253,7 +253,7 @@ func TestTargetAllocatorValidatingWebhook(t *testing.T) {
 						Ports: []v1beta1.PortsSpec{
 							{
 								ServicePort: v1.ServicePort{
-									// this port name contains a non alphanumeric character, which is invalid.
+									// this port name contains a non-alphanumeric character, which is invalid.
 									Name:     "-test🦄port",
 									Port:     12345,
 									Protocol: v1.ProtocolTCP,
diff --git a/apis/v1beta1/collector_webhook_test.go b/apis/v1beta1/collector_webhook_test.go
index f7f5d5d6..8d482aa8 100644
--- a/apis/v1beta1/collector_webhook_test.go
+++ b/apis/v1beta1/collector_webhook_test.go
@@ -838,7 +838,7 @@ func TestOTELColValidatingWebhook(t *testing.T) {
 						Ports: []v1beta1.PortsSpec{
 							{
 								ServicePort: v1.ServicePort{
-									// this port name contains a non alphanumeric character, which is invalid.
+									// this port name contains a non-alphanumeric character, which is invalid.
 									Name:     "-test🦄port",
 									Port:     12345,
 									Protocol: v1.ProtocolTCP,
diff --git a/cmd/otel-allocator/benchmark_test.go b/cmd/otel-allocator/benchmark_test.go
index a5258d61..f39f60df 100644
--- a/cmd/otel-allocator/benchmark_test.go
+++ b/cmd/otel-allocator/benchmark_test.go
@@ -31,7 +31,7 @@ import (
 var targetCounts = []int{1000, 10000, 100000, 800000}
 
 // BenchmarkProcessTargets benchmarks the whole target allocation pipeline. It starts with data the prometheus
-// discovery manager would normally output, and pushes it all the way into the allocator. It notably doe *not* check
+// discovery manager would normally output, and pushes it all the way into the allocator. It notably does *not* check
 // the HTTP server afterward. Test data is chosen to be reasonably representative of what the Prometheus service discovery
 // outputs in the real world.
 func BenchmarkProcessTargets(b *testing.B) {
diff --git a/cmd/otel-allocator/internal/config/config_test.go b/cmd/otel-allocator/internal/config/config_test.go
index 21221e80..9df390f4 100644
--- a/cmd/otel-allocator/internal/config/config_test.go
+++ b/cmd/otel-allocator/internal/config/config_test.go
@@ -53,6 +53,7 @@ func TestLoad(t *testing.T) {
 					ScrapeConfigNamespaceSelector:   &metav1.LabelSelector{},
 					ProbeNamespaceSelector:          &metav1.LabelSelector{},
 				},
+				CollectorNotReadyGracePeriod: 30 * time.Second,
 				HTTPS: HTTPSServerConfig{
 					Enabled:         true,
 					ListenAddr:      ":8443",
diff --git a/cmd/otel-allocator/internal/config/testdata/config_test.yaml b/cmd/otel-allocator/internal/config/testdata/config_test.yaml
index 809f2880..1b6f4c39 100644
--- a/cmd/otel-allocator/internal/config/testdata/config_test.yaml
+++ b/cmd/otel-allocator/internal/config/testdata/config_test.yaml
@@ -6,6 +6,7 @@ collector_selector:
 prometheus_cr:
   enabled: true
   scrape_interval: 60s
+collector_not_ready_grace_period: 30s
 https:
   enabled: true
   listen_addr: :8443
diff --git a/internal/autodetect/main_test.go b/internal/autodetect/main_test.go
index 35cd8ec9..81016baf 100644
--- a/internal/autodetect/main_test.go
+++ b/internal/autodetect/main_test.go
@@ -233,7 +233,7 @@ func TestDetectRBACPermissionsBasedOnAvailableClusterRoles(t *testing.T) {
 		},
 	} {
 		t.Run(tt.description, func(t *testing.T) {
-			// These settings can be get from env vars
+			// These settings can be gotten from env vars
 			t.Setenv(autodetectutils.NAMESPACE_ENV_VAR, tt.namespace)
 			t.Setenv(autodetectutils.SA_ENV_VAR, tt.serviceAccount)
 
diff --git a/internal/manifests/targetallocator/configmap_test.go b/internal/manifests/targetallocator/configmap_test.go
index c8801fbe..d643be09 100644
--- a/internal/manifests/targetallocator/configmap_test.go
+++ b/internal/manifests/targetallocator/configmap_test.go
@@ -728,3 +728,46 @@ func TestGetGlobalConfig(t *testing.T) {
 		})
 	}
 }
+
+func TestGetCollectorNotReadyGracePeriod(t *testing.T) {
+	collector := collectorInstance()
+	targetAllocator := targetAllocatorInstanceWithCollectorNotReadyGracePeriod()
+	cfg := config.New()
+	params := Params{
+		Collector:       collector,
+		TargetAllocator: targetAllocator,
+		Config:          cfg,
+		Log:             logr.Discard(),
+	}
+
+	t.Run("should return expected target allocator config map with collector_not_ready_grace_period", func(t *testing.T) {
+		expectedData := map[string]string{
+			targetAllocatorFilename: `allocation_fallback_strategy: consistent-hashing
+allocation_strategy: consistent-hashing
+collector_not_ready_grace_period: 30s
+collector_selector:
+  matchlabels:
+    app.kubernetes.io/component: opentelemetry-collector
+    app.kubernetes.io/instance: default.my-instance
+    app.kubernetes.io/managed-by: opentelemetry-operator
+    app.kubernetes.io/part-of: opentelemetry
+  matchexpressions: []
+config:
+  scrape_configs:
+  - job_name: otel-collector
+    scrape_interval: 10s
+    static_configs:
+    - targets:
+      - 0.0.0.0:8888
+      - 0.0.0.0:9999
+filter_strategy: relabel-config
+`,
+		}
+
+		actual, err := ConfigMap(params)
+		require.NoError(t, err)
+
+		assert.Equal(t, "my-instance-targetallocator", actual.Name)
+		assert.Equal(t, expectedData[targetAllocatorFilename], actual.Data[targetAllocatorFilename])
+	})
+}
diff --git a/internal/manifests/targetallocator/deployment_test.go b/internal/manifests/targetallocator/deployment_test.go
index 1f5672a9..ca2543ad 100644
--- a/internal/manifests/targetallocator/deployment_test.go
+++ b/internal/manifests/targetallocator/deployment_test.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"os"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -208,6 +209,16 @@ func targetAllocatorInstance() v1alpha1.TargetAllocator {
 	return *targetAllocator
 }
 
+func targetAllocatorInstanceWithCollectorNotReadyGracePeriod() v1alpha1.TargetAllocator {
+	collectorInstance := collectorInstance()
+	collectorInstance.Spec.TargetAllocator.Enabled = true
+	params := manifests.Params{OtelCol: *collectorInstance}
+	targetAllocator, _ := collector.TargetAllocator(params)
+	targetAllocator.Spec.Image = "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-targetallocator:0.47.0"
+	targetAllocator.Spec.CollectorNotReadyGracePeriod = &metav1.Duration{Duration: 30 * time.Second}
+	return *targetAllocator
+}
+
 func TestDeploymentNodeSelector(t *testing.T) {
 	// Test default
 	targetAllocator1 := v1alpha1.TargetAllocator{}
diff --git a/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml b/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml
index 6ab2e4ac..73471e36 100644
--- a/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml
+++ b/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml
@@ -3735,7 +3735,7 @@ spec:
                       type: array
                     minReplicas:
                       description: MinReplicas sets a lower bound to the autoscaling
-                        feature.  Set this if your are using autoscaling. It must be
+                        feature.  Set this if you are using autoscaling. It must be
                         at least 1
                       format: int32
                       type: integer
@@ -5554,7 +5554,7 @@ spec:
                   type: string
                 replicas:
                   description: Replicas is the number of pod instances for the underlying
-                    OpenTelemetry Collector. Set this if your are not using autoscaling
+                    OpenTelemetry Collector. Set this if you are not using autoscaling
                   format: int32
                   type: integer
                 resources: