1
1
package state
2
2
3
3
import (
4
- "encoding/json"
5
4
"fmt"
6
- "os"
7
- "path/filepath"
8
- "strings"
9
5
"sync"
10
6
11
7
"github.com/go-kit/kit/log"
@@ -19,9 +15,6 @@ import (
19
15
20
16
"github.com/spf13/afero"
21
17
"github.com/spf13/viper"
22
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23
- "k8s.io/client-go/kubernetes"
24
- "k8s.io/client-go/rest"
25
18
)
26
19
27
20
type Manager interface {
@@ -50,6 +43,11 @@ type Manager interface {
50
43
AddCA (name string , newCA util.CAType ) error
51
44
}
52
45
46
+ type stateSerializer interface {
47
+ Load () (State , error )
48
+ Save (State ) error
49
+ }
50
+
53
51
var _ Manager = & MManager {}
54
52
55
53
// MManager is the saved output of a plan run to load on future runs
@@ -241,118 +239,54 @@ func (m *MManager) SerializeUpstreamContents(contents *UpstreamContents) error {
241
239
return err
242
240
}
243
241
244
- // TryLoad will attempt to load a state file from disk, if present
245
- func (m * MManager ) TryLoad () (State , error ) {
246
- m .StateRWMut .RLock ()
247
- defer m .StateRWMut .RUnlock ()
242
+ func (m * MManager ) getStateSerializer () (stateSerializer , error ) {
248
243
stateFrom := m .V .GetString ("state-from" )
249
244
if stateFrom == "" {
250
245
stateFrom = "file"
251
246
}
252
247
253
- // TODO consider an interface
254
-
255
248
switch stateFrom {
256
249
case "file" :
257
- return m . tryLoadFromFile ()
250
+ return newFileSerializer ( m . FS , m . Logger ), nil
258
251
case "secret" :
259
- return m .tryLoadFromSecret ()
252
+ return newSecretSerializer (m .Logger , m .V .GetString ("secret-namespace" ), m .V .GetString ("secret-name" ), m .V .GetString ("secret-key" )), nil
253
+ case "url" :
254
+ return newURLSerializer (m .Logger , m .V .GetString ("state-get-url" ), m .V .GetString ("state-put-url" )), nil
260
255
default :
261
- err := fmt .Errorf ("unsupported state-from value: %q" , stateFrom )
262
- return State {}, errors .Wrap (err , "try load state" )
256
+ return nil , fmt .Errorf ("unsupported state-from value: %q" , stateFrom )
263
257
}
264
258
}
265
259
266
- // ResetLifecycle is used by `ship update --headed` to reset the saved stepsCompleted
267
- // in the state.json
268
- func (m * MManager ) ResetLifecycle () error {
269
- debug := level .Debug (log .With (m .Logger , "method" , "ResetLifecycle" ))
270
-
271
- debug .Log ("event" , "safeStateUpdate" )
272
- _ , err := m .StateUpdate (func (state State ) (State , error ) {
273
-
274
- state .V1 .Lifecycle = nil
275
- return state , nil
276
- })
277
- return err
278
- }
279
-
280
- // tryLoadFromSecret will attempt to load the state from a secret
281
- // currently only supports in-cluster execution
282
- func (m * MManager ) tryLoadFromSecret () (State , error ) {
283
- config , err := rest .InClusterConfig ()
284
- if err != nil {
285
- return State {}, errors .Wrap (err , "get in cluster config" )
286
- }
260
+ // TryLoad will attempt to load a state file from disk, if present
261
+ func (m * MManager ) TryLoad () (State , error ) {
262
+ m .StateRWMut .RLock ()
263
+ defer m .StateRWMut .RUnlock ()
287
264
288
- clientset , err := kubernetes . NewForConfig ( config )
265
+ s , err := m . getStateSerializer ( )
289
266
if err != nil {
290
- return State {}, errors .Wrap (err , "get kubernetes client" )
291
- }
292
-
293
- ns := m .V .GetString ("secret-namespace" )
294
- if ns == "" {
295
- return State {}, errors .New ("secret-namespace is not set" )
296
- }
297
- secretName := m .V .GetString ("secret-name" )
298
- if secretName == "" {
299
- return State {}, errors .New ("secret-name is not set" )
300
- }
301
- secretKey := m .V .GetString ("secret-key" )
302
- if secretKey == "" {
303
- return State {}, errors .New ("secret-key is not set" )
267
+ return State {}, errors .Wrap (err , "create state serializer" )
304
268
}
305
269
306
- secret , err := clientset . CoreV1 (). Secrets ( ns ). Get ( secretName , metav1. GetOptions {} )
270
+ state , err := s . Load ( )
307
271
if err != nil {
308
- return State {}, errors .Wrap (err , "get secret" )
309
- }
310
-
311
- serialized , ok := secret .Data [secretKey ]
312
- if ! ok {
313
- err := fmt .Errorf ("key %q not found in secret %q" , secretKey , secretName )
314
- return State {}, errors .Wrap (err , "get state from secret" )
272
+ return State {}, errors .Wrap (err , "load state" )
315
273
}
316
274
317
- // An empty secret should be treated as empty state
318
- if len (strings .TrimSpace (string (serialized ))) == 0 {
319
- return State {}, nil
320
- }
321
-
322
- var state State
323
- if err := json .Unmarshal (serialized , & state ); err != nil {
324
- return State {}, errors .Wrap (err , "unmarshal state" )
325
- }
326
-
327
- level .Debug (m .Logger ).Log (
328
- "event" , "state.unmarshal" ,
329
- "type" , "versioned" ,
330
- "source" , "secret" ,
331
- "value" , fmt .Sprintf ("%+v" , state ),
332
- )
333
-
334
- level .Debug (m .Logger ).Log ("event" , "state.resolve" , "type" , "versioned" )
335
275
return state , nil
336
276
}
337
277
338
- func (m * MManager ) tryLoadFromFile () (State , error ) {
339
- if _ , err := m .FS .Stat (constants .StatePath ); os .IsNotExist (err ) {
340
- level .Debug (m .Logger ).Log ("msg" , "no saved state exists" , "path" , constants .StatePath )
341
- return State {}, nil
342
- }
343
-
344
- serialized , err := m .FS .ReadFile (constants .StatePath )
345
- if err != nil {
346
- return State {}, errors .Wrap (err , "read state file" )
347
- }
278
+ // ResetLifecycle is used by `ship update --headed` to reset the saved stepsCompleted
279
+ // in the state.json
280
+ func (m * MManager ) ResetLifecycle () error {
281
+ debug := level .Debug (log .With (m .Logger , "method" , "ResetLifecycle" ))
348
282
349
- var state State
350
- if err := json .Unmarshal (serialized , & state ); err != nil {
351
- return State {}, errors .Wrap (err , "unmarshal state" )
352
- }
283
+ debug .Log ("event" , "safeStateUpdate" )
284
+ _ , err := m .StateUpdate (func (state State ) (State , error ) {
353
285
354
- level .Debug (m .Logger ).Log ("event" , "state.resolve" , "type" , "versioned" )
355
- return state , nil
286
+ state .V1 .Lifecycle = nil
287
+ return state , nil
288
+ })
289
+ return err
356
290
}
357
291
358
292
func (m * MManager ) SaveKustomize (kustomize * Kustomize ) error {
@@ -385,76 +319,15 @@ func (m *MManager) RemoveStateFile() error {
385
319
func (m * MManager ) serializeAndWriteState (state State ) error {
386
320
m .StateRWMut .Lock ()
387
321
defer m .StateRWMut .Unlock ()
388
- debug := level .Debug (log .With (m .Logger , "method" , "serializeAndWriteState" ))
389
322
state = state .migrateDeprecatedFields ()
390
323
391
- stateFrom := m .V .GetString ("state-from" )
392
- if stateFrom == "" {
393
- stateFrom = "file"
394
- }
395
-
396
- debug .Log ("stateFrom" , stateFrom )
397
-
398
- switch stateFrom {
399
- case "file" :
400
- return m .serializeAndWriteStateFile (state )
401
- case "secret" :
402
- return m .serializeAndWriteStateSecret (state )
403
- default :
404
- err := fmt .Errorf ("unsupported state-from value: %q" , stateFrom )
405
- return errors .Wrap (err , "serializeAndWriteState" )
406
- }
407
- }
408
-
409
- func (m * MManager ) serializeAndWriteStateFile (state State ) error {
410
-
411
- serialized , err := json .MarshalIndent (state , "" , " " )
412
- if err != nil {
413
- return errors .Wrap (err , "serialize state" )
414
- }
415
-
416
- err = m .FS .MkdirAll (filepath .Dir (constants .StatePath ), 0700 )
417
- if err != nil {
418
- return errors .Wrap (err , "mkdir state" )
419
- }
420
-
421
- err = m .FS .WriteFile (constants .StatePath , serialized , 0644 )
422
- if err != nil {
423
- return errors .Wrap (err , "write state file" )
424
- }
425
-
426
- return nil
427
- }
428
-
429
- func (m * MManager ) serializeAndWriteStateSecret (state State ) error {
430
- serialized , err := json .MarshalIndent (state , "" , " " )
324
+ s , err := m .getStateSerializer ()
431
325
if err != nil {
432
- return errors .Wrap (err , "serialize state" )
326
+ return errors .Wrap (err , "create state serializer " )
433
327
}
434
328
435
- config , err := rest .InClusterConfig ()
436
- if err != nil {
437
- return errors .Wrap (err , "get in cluster config" )
438
- }
439
-
440
- clientset , err := kubernetes .NewForConfig (config )
441
- if err != nil {
442
- return errors .Wrap (err , "get kubernetes client" )
443
- }
444
-
445
- secret , err := clientset .CoreV1 ().Secrets (m .V .GetString ("secret-namespace" )).Get (m .V .GetString ("secret-name" ), metav1.GetOptions {})
446
- if err != nil {
447
- return errors .Wrap (err , "get secret" )
448
- }
449
-
450
- secret .Data [m .V .GetString ("secret-key" )] = serialized
451
- debug := level .Debug (log .With (m .Logger , "method" , "serializeHelmValues" ))
452
-
453
- debug .Log ("event" , "serializeAndWriteStateSecret" , "name" , secret .Name , "key" , m .V .GetString ("secret-key" ))
454
-
455
- _ , err = clientset .CoreV1 ().Secrets (m .V .GetString ("secret-namespace" )).Update (secret )
456
- if err != nil {
457
- return errors .Wrap (err , "update secret" )
329
+ if err := s .Save (state ); err != nil {
330
+ return errors .Wrap (err , "save state" )
458
331
}
459
332
460
333
return nil
0 commit comments