cmd/k8s-operator,ipn/store/kubestore: patch secrets instead of updating

We would call Update on the secret, but that was racey and would occasionaly
fail. Instead use patch whenever we can.

Fixes errors like
```
boot: 2023/08/29 01:03:53 failed to set serve config: sending serve config: updating config: writing ServeConfig to StateStore: Operation cannot be fulfilled on secrets "ts-webdav-kfrzv-0": the object has been modified; please apply your changes to the latest version and try again

{"level":"error","ts":"2023-08-29T01:03:48Z","msg":"Reconciler error","controller":"ingress","controllerGroup":"networking.k8s.io","controllerKind":"Ingress","Ingress":{"name":"webdav","namespace":"default"},"namespace":"default","name":"webdav","reconcileID":"96f5cfed-7782-4834-9b75-b0950fd563ed","error":"failed to provision: failed to create or get API key secret: Operation cannot be fulfilled on secrets \"ts-webdav-kfrzv-0\": the object has been modified; please apply your changes to the latest version and try again","stacktrace":"sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).reconcileHandler\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:324\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).processNextWorkItem\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:265\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).Start.func2.2\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:226"}
```

Updates #502
Updates #7895

Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
Maisem Ali
2023-08-29 12:43:22 -07:00
committed by Maisem Ali
parent 930e6f68f2
commit c919ff540f
3 changed files with 28 additions and 8 deletions

View File

@@ -19,6 +19,7 @@ import (
// Store is an ipn.StateStore that uses a Kubernetes Secret for persistence.
type Store struct {
client *kube.Client
canPatch bool
secretName string
}
@@ -28,8 +29,13 @@ func New(_ logger.Logf, secretName string) (*Store, error) {
if err != nil {
return nil, err
}
canPatch, err := c.CheckSecretPermissions(context.Background(), secretName)
if err != nil {
return nil, err
}
return &Store{
client: c,
canPatch: canPatch,
secretName: secretName,
}, nil
}
@@ -93,6 +99,19 @@ func (s *Store) WriteState(id ipn.StateKey, bs []byte) error {
}
return err
}
if s.canPatch {
m := []kube.JSONPatch{
{
Op: "add",
Path: "/data/" + sanitizeKey(id),
Value: bs,
},
}
if err := s.client.JSONPatchSecret(ctx, s.secretName, m); err != nil {
return err
}
return nil
}
secret.Data[sanitizeKey(id)] = bs
if err := s.client.UpdateSecret(ctx, secret); err != nil {
return err