Skip to content

Bug: copySpecFromSliceConfigToWorkerSlice silently returns empty spec on copier failureΒ #372

Description

@Elvand-Lie

πŸ“œ Description

copySpecFromSliceConfigToWorkerSlice in service/worker_slice_config_service.go at lines 687–694 uses copier.Copy to deep-copy the SliceConfig spec into a WorkerSliceConfig. If the copy fails, the function silently returns an empty WorkerSliceConfig{} with a zeroed spec. The caller then overwrites the existing worker slice config with this empty spec and pushes it to the API server.

// service/worker_slice_config_service.go, lines 686-694
func (s *WorkerSliceConfigService) copySpecFromSliceConfigToWorkerSlice(ctx context.Context,
    sliceConfig controllerv1alpha1.SliceConfig) workerv1alpha1.WorkerSliceConfig {
    slice := workerv1alpha1.WorkerSliceConfig{}
    err := copier.Copy(&slice.Spec, &sliceConfig.Spec)
    if err != nil {
        return workerv1alpha1.WorkerSliceConfig{}  // ← empty spec, error discarded
    }
    return slice
}

The caller at lines 165–166 and 282:

// line 165-166
slice := s.copySpecFromSliceConfigToWorkerSlice(ctx, *sliceConfig)
workerSliceConfig.Spec = slice.Spec  // overwrites with empty spec if copier failed

// ... fields are set on top of the (possibly empty) spec ...

// line 282
err = util.UpdateResource(ctx, workerSliceConfig)  // pushes to API server

If copier.Copy fails, slice.Spec is a zero-valued WorkerSliceConfigSpec. The caller overwrites workerSliceConfig.Spec with this empty struct, losing all existing configuration. Some fields are re-set afterward (Octet, ClusterSubnetCIDR, SliceName at lines 279–281), but critical fields from the SliceConfig spec β€” such as SliceType, SliceSubnet, SliceIpamType, Clusters, MaxClusters, and the entire SliceGatewayProvider β€” are zeroed.

πŸ‘Ÿ Reproduction steps

  1. Create a SliceConfig with a full spec (QoS profile, external gateway config, namespace isolation, etc.)

  2. Register clusters and wait for WorkerSliceConfigs to be created and fully reconciled.

  3. Trigger a copier.Copy failure. While unlikely in normal operation, this can occur when:

    • A dependency update changes struct field types or tags that copier relies on
    • Memory pressure causes allocation failures during the deep copy
    • A custom type implements an interface that copier doesn't handle correctly
  4. The reconciler calls copySpecFromSliceConfigToWorkerSlice, which returns an empty WorkerSliceConfig{}.

  5. The caller overwrites workerSliceConfig.Spec with the empty spec, then calls util.UpdateResource. The API server receives a WorkerSliceConfig with most spec fields zeroed.

  6. The worker-operator on the affected cluster picks up the change and reconfigures the slice with empty/zero values.

πŸ‘ Expected behavior

The function should return an error when copier.Copy fails. The caller should abort the reconciliation and return the error to controller-runtime for requeue with exponential backoff. The existing WorkerSliceConfig should remain untouched.

πŸ‘Ž Actual Behavior

The function silently returns an empty spec. The caller writes this to the API server, wiping the worker's slice configuration. No error is logged, no event is recorded, and no metric is incremented.

Downstream effects:

  • SliceSubnet, SliceType, SliceIpamType are zeroed β€” the worker loses its network identity
  • QoS profile is zeroed β€” traffic shaping is removed
  • NamespaceIsolationProfile is zeroed β€” namespace isolation policies are dropped
  • ExternalGatewayConfig is zeroed β€” external connectivity is broken
  • The worker-operator applies these zero values, disrupting all workloads on the slice

🐚 Relevant log output

No error is logged. The copier failure is silently discarded.
The only observable symptom is the WorkerSliceConfig on the API server
suddenly having empty/zero-valued spec fields:

$ kubectl get workersliceconfig test-slice-cluster-a -n kubeslice-my-project -o yaml
spec:
  sliceName: test-slice
  octet: 0
  clusterSubnetCIDR: ""
  sliceSubnet: ""        # ← was "10.1.0.0/16"
  sliceType: ""          # ← was "Application"
  # ... all other fields zeroed ...

Version

master branch (latest HEAD as of 2026-05-15). The bug exists since the initial implementation of WorkerSliceConfig reconciliation.

πŸ–₯️ What operating system are you seeing the problem on?

No response

βœ… Proposed Solution

Change the function signature to return an error, and propagate it to the caller.

Step 1: Update copySpecFromSliceConfigToWorkerSlice (lines 687–694):

func (s *WorkerSliceConfigService) copySpecFromSliceConfigToWorkerSlice(ctx context.Context,
    sliceConfig controllerv1alpha1.SliceConfig) (workerv1alpha1.WorkerSliceConfig, error) {
    slice := workerv1alpha1.WorkerSliceConfig{}
    err := copier.Copy(&slice.Spec, &sliceConfig.Spec)
    if err != nil {
        return workerv1alpha1.WorkerSliceConfig{}, fmt.Errorf("failed to copy SliceConfig spec to WorkerSliceConfig: %w", err)
    }
    return slice, nil
}

Step 2: Update the caller at line 165:

slice, err := s.copySpecFromSliceConfigToWorkerSlice(ctx, *sliceConfig)
if err != nil {
    logger.With(zap.Error(err)).Errorf("Failed to copy spec from SliceConfig to WorkerSliceConfig")
    return ctrl.Result{}, err
}
workerSliceConfig.Spec = slice.Spec

This ensures the reconciler aborts and requeues on copy failure, leaving the existing WorkerSliceConfig intact.

πŸ‘€ Have you spent some time to check if this issue has been raised before?

  • I checked and didn't find any similar issue

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions