Skip to content

Commit 9d6b685

Browse files
committed
Iter
1 parent 05097ba commit 9d6b685

File tree

8 files changed

+437
-65
lines changed

8 files changed

+437
-65
lines changed

pkg/deployment/reconcile/action_license_generate.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package reconcile
2222

2323
import (
2424
"context"
25+
"encoding/base64"
2526
"fmt"
2627
"math"
2728
"time"
@@ -32,6 +33,7 @@ import (
3233

3334
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
3435
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
36+
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
3537
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
3638
lmanager "github.com/arangodb/kube-arangodb/pkg/license_manager"
3739
"github.com/arangodb/kube-arangodb/pkg/platform/inventory"
@@ -216,6 +218,25 @@ func (a *actionLicenseGenerate) Start(ctx context.Context) (bool, error) {
216218
}
217219
}
218220

221+
if spec.Sync.IsEnabled() {
222+
if !spec.License.HasSecretName() {
223+
a.log.Debug("License Secret Gone")
224+
return true, nil
225+
}
226+
s, ok := cache.Secret().V1().GetSimple(spec.License.GetSecretName())
227+
if !ok {
228+
a.log.Debug("License Secret Gone")
229+
return true, nil
230+
}
231+
232+
if _, _, err := patcher.Patcher[*core.Secret](ctx, cache.Client().Kubernetes().CoreV1().Secrets(a.actionCtx.GetNamespace()), s, meta.PatchOptions{}, func(in *core.Secret) []patch.Item {
233+
return []patch.Item{patch.ItemReplace(patch.NewPath("data", utilConstants.SecretKeyToken), base64.StdEncoding.EncodeToString([]byte(generatedLicense.License)))}
234+
}); err != nil {
235+
a.log.Err(err).Debug("Failed to patch License Secret")
236+
return true, nil
237+
}
238+
}
239+
219240
if err := a.actionCtx.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
220241
s.License = &api.DeploymentStatusLicense{
221242
ID: generatedLicense.ID,

pkg/deployment/reconcile/plan_builder_license.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ func (r *Reconciler) updateClusterLicenseDiscover(spec api.DeploymentSpec, conte
107107
return "", err
108108
}
109109

110-
if l.V2.IsV2Set() {
111-
return api.LicenseModeKey, nil
112-
}
113-
114110
if l.API != nil {
115111
return api.LicenseModeAPI, nil
116112
}
117113

114+
if l.V2.IsV2Set() {
115+
return api.LicenseModeKey, nil
116+
}
117+
118118
return "", errors.Errorf("Unable to discover License mode")
119119
}
120120

@@ -256,5 +256,12 @@ func (r *Reconciler) updateClusterLicenseAPI(ctx context.Context, spec api.Deplo
256256
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Registry Change Required")}
257257
}
258258

259+
if spec.Sync.IsEnabled() {
260+
// Feedback of the token is required
261+
if l.V2.V2Hash() != status.License.Hash {
262+
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Sync Token Required")}
263+
}
264+
}
265+
259266
return nil
260267
}

pkg/platform/pack/cache.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package pack
22+
23+
import (
24+
"crypto/sha256"
25+
"fmt"
26+
"hash"
27+
"io"
28+
"os"
29+
"path"
30+
"sync"
31+
32+
"github.com/pkg/errors"
33+
)
34+
35+
func NewCache(path string) Cache {
36+
return &cache{
37+
lock: sync.Mutex{},
38+
path: path,
39+
files: map[string]string{},
40+
writers: map[string]*cacheWriter{},
41+
}
42+
}
43+
44+
type Cache interface {
45+
CacheObject(checksum string, path string, args ...any) (io.WriteCloser, error)
46+
47+
Get(checksum string, path string, args ...any) (io.ReadCloser, error)
48+
49+
Saved() int
50+
}
51+
52+
type cache struct {
53+
lock sync.Mutex
54+
55+
path string
56+
57+
saved int
58+
59+
files map[string]string
60+
61+
writers map[string]*cacheWriter
62+
}
63+
64+
func (c *cache) Saved() int {
65+
return c.saved
66+
}
67+
68+
func (c *cache) Get(checksum string, p string, args ...any) (io.ReadCloser, error) {
69+
c.lock.Lock()
70+
defer c.lock.Unlock()
71+
72+
z := fmt.Sprintf(p, args...)
73+
74+
if v, ok := c.files[z]; !ok || v != checksum {
75+
return nil, os.ErrNotExist
76+
}
77+
78+
return os.Open(path.Join(c.path, z))
79+
}
80+
81+
func (c *cache) CacheObject(checksum string, p string, args ...any) (io.WriteCloser, error) {
82+
c.lock.Lock()
83+
defer c.lock.Unlock()
84+
85+
z := fmt.Sprintf(p, args...)
86+
87+
if v, ok := c.files[z]; ok {
88+
if v == checksum {
89+
return nil, os.ErrExist
90+
} else {
91+
return nil, errors.Errorf("cache object %s already exists with checksum %s, expected %s", z, v, checksum)
92+
}
93+
}
94+
95+
if _, ok := c.writers[z]; ok {
96+
return nil, nil
97+
}
98+
99+
if current, err := c.readChecksum(p, args...); err != nil {
100+
if !os.IsNotExist(err) {
101+
return nil, err
102+
}
103+
} else if current == checksum {
104+
c.files[z] = checksum
105+
return nil, os.ErrExist
106+
}
107+
108+
fd, err := os.CreateTemp("", "tmp-")
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
q := &cacheWriter{
114+
cache: c,
115+
remote: fd,
116+
dest: z,
117+
hash: checksum,
118+
currentHash: sha256.New(),
119+
}
120+
121+
c.writers[z] = q
122+
123+
return q, nil
124+
}
125+
126+
func (c *cache) readChecksum(p string, args ...any) (string, error) {
127+
f, err := os.Open(path.Join(c.path, fmt.Sprintf(p, args...)))
128+
if err != nil {
129+
return "", err
130+
}
131+
132+
defer f.Close()
133+
134+
h := sha256.New()
135+
if _, err := io.Copy(h, f); err != nil {
136+
return "", err
137+
}
138+
139+
return fmt.Sprintf("%x", h.Sum(nil)), nil
140+
}
141+
142+
func (c *cache) complete(z *cacheWriter) error {
143+
if err := os.MkdirAll(path.Dir(path.Join(c.path, z.dest)), 0755); err != nil {
144+
if !os.IsExist(err) {
145+
return err
146+
}
147+
}
148+
149+
if _, err := z.remote.Seek(0, 0); err != nil {
150+
return err
151+
}
152+
153+
hash := fmt.Sprintf("%x", z.currentHash.Sum(nil))
154+
155+
if hash != z.hash {
156+
return errors.Errorf("checksum mismatch for %s != %s", hash, z.hash)
157+
}
158+
159+
out, err := os.OpenFile(path.Join(c.path, z.dest), os.O_CREATE|os.O_RDWR, 0644)
160+
if err != nil {
161+
return err
162+
}
163+
164+
defer out.Close()
165+
166+
if _, err := io.Copy(out, z.remote); err != nil {
167+
return err
168+
}
169+
170+
if err := z.remote.Close(); err != nil {
171+
return err
172+
}
173+
174+
if err := os.Remove(z.remote.Name()); err != nil {
175+
return err
176+
}
177+
178+
c.lock.Lock()
179+
defer c.lock.Unlock()
180+
181+
c.saved += 1
182+
183+
c.files[z.dest] = hash
184+
185+
delete(c.writers, z.dest)
186+
187+
return nil
188+
}
189+
190+
type cacheWriter struct {
191+
lock sync.Mutex
192+
193+
cache *cache
194+
195+
remote *os.File
196+
currentHash hash.Hash
197+
198+
dest string
199+
hash string
200+
}
201+
202+
func (c *cacheWriter) Write(p []byte) (n int, err error) {
203+
c.lock.Lock()
204+
defer c.lock.Unlock()
205+
206+
n, err = c.remote.Write(p)
207+
if err != nil {
208+
return n, err
209+
}
210+
211+
c.currentHash.Write(p[:n])
212+
213+
return n, nil
214+
}
215+
216+
func (c *cacheWriter) Close() error {
217+
c.lock.Lock()
218+
defer c.lock.Unlock()
219+
220+
return c.cache.complete(c)
221+
}

pkg/platform/pack/cache_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package pack
22+
23+
import (
24+
"os"
25+
"testing"
26+
27+
"github.com/stretchr/testify/require"
28+
29+
"github.com/arangodb/kube-arangodb/pkg/util"
30+
)
31+
32+
func TestCache(t *testing.T) {
33+
c := NewCache(t.TempDir())
34+
35+
var d = make([]byte, 1024)
36+
37+
checksum := util.SHA256(d)
38+
39+
_, err := c.Get(checksum, "some/file")
40+
require.ErrorAs(t, err, &os.ErrNotExist)
41+
42+
t.Run("Invalid Sha", func(t *testing.T) {
43+
out, err := c.CacheObject("ABCD", "some/file")
44+
require.NoError(t, err)
45+
46+
z, err := out.Write(d)
47+
require.NoError(t, err)
48+
require.Len(t, d, z)
49+
50+
require.Error(t, out.Close())
51+
52+
require.EqualValues(t, 0, c.Saved())
53+
})
54+
55+
t.Run("Valid Sha", func(t *testing.T) {
56+
out, err := c.CacheObject(util.SHA256(d), "some/file")
57+
require.NoError(t, err)
58+
59+
z, err := out.Write(d)
60+
require.NoError(t, err)
61+
require.Len(t, d, z)
62+
63+
require.NoError(t, out.Close())
64+
65+
require.EqualValues(t, 1, c.Saved())
66+
})
67+
68+
t.Run("Multi Upload Sha", func(t *testing.T) {
69+
out, err := c.CacheObject(util.SHA256(d), "some/file2")
70+
require.NoError(t, err)
71+
72+
nout, err := c.CacheObject(util.SHA256(d), "some/file2")
73+
require.NoError(t, err)
74+
require.Nil(t, nout)
75+
76+
z, err := out.Write(d)
77+
require.NoError(t, err)
78+
require.Len(t, d, z)
79+
80+
require.NoError(t, out.Close())
81+
82+
require.EqualValues(t, 1, c.Saved())
83+
})
84+
}

0 commit comments

Comments
 (0)