@@ -6,16 +6,20 @@ package cmdmigrate
6
6
import (
7
7
"bytes"
8
8
"context"
9
- "errors"
9
+ goerrors "errors"
10
10
"fmt"
11
11
"io"
12
12
"io/ioutil"
13
13
"os"
14
+ "path/filepath"
14
15
15
16
"github.com/GoogleContainerTools/kpt/internal/cmdliveinit"
16
17
"github.com/GoogleContainerTools/kpt/internal/docs/generated/livedocs"
18
+ "github.com/GoogleContainerTools/kpt/internal/errors"
17
19
"github.com/GoogleContainerTools/kpt/internal/pkg"
20
+ "github.com/GoogleContainerTools/kpt/internal/types"
18
21
"github.com/GoogleContainerTools/kpt/internal/util/argutil"
22
+ "github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil"
19
23
"github.com/GoogleContainerTools/kpt/pkg/live"
20
24
"github.com/spf13/cobra"
21
25
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -39,10 +43,12 @@ type MigrateRunner struct {
39
43
dir string
40
44
dryRun bool
41
45
name string
46
+ rgFile string
42
47
force bool
43
48
rgInvClientFunc func (util.Factory ) (inventory.InventoryClient , error )
44
49
cmInvClientFunc func (util.Factory ) (inventory.InventoryClient , error )
45
50
cmLoader manifestreader.ManifestLoader
51
+ cmNotMigrated bool // flag to determine if migration from ConfigMap has occurred
46
52
}
47
53
48
54
// NewRunner returns a pointer to an initial MigrateRunner structure.
@@ -96,6 +102,15 @@ func NewCommand(ctx context.Context, f util.Factory, cmLoader manifestreader.Man
96
102
// Run executes the migration from the ConfigMap based inventory to the ResourceGroup
97
103
// based inventory.
98
104
func (mr * MigrateRunner ) Run (reader io.Reader , args []string ) error {
105
+ // Use ResourceGroup file for inventory logic if the resourcegroup file
106
+ // is set directly. For this feature gate, the resourcegroup must be directly set
107
+ // through our tests since we are not exposing this through the command surface as a
108
+ // flag, currently. When we promote this, the resourcegroup filename can be empty and
109
+ // the default filename value will be inferred/used.
110
+ if mr .rgFile != "" {
111
+ return mr .runLiveMigrateWithRGFile (reader , args )
112
+ }
113
+
99
114
// Validate the number of arguments.
100
115
if len (args ) > 1 {
101
116
return fmt .Errorf ("too many arguments; migrate requires one directory argument (or stdin)" )
@@ -384,3 +399,176 @@ func rgInvClient(factory util.Factory) (inventory.InventoryClient, error) {
384
399
func cmInvClient (factory util.Factory ) (inventory.InventoryClient , error ) {
385
400
return inventory .NewInventoryClient (factory , inventory .WrapInventoryObj , inventory .InvInfoToConfigMap )
386
401
}
402
+
403
+ // func runLiveMigrateWithRGFile is a modified version of MigrateRunner.Run that stores the
404
+ // package inventory information in a separate resourcegroup file. The logic for this is branched into
405
+ // a separate function to enable feature gating.
406
+ func (mr * MigrateRunner ) runLiveMigrateWithRGFile (reader io.Reader , args []string ) error {
407
+ // Validate the number of arguments.
408
+ if len (args ) > 1 {
409
+ return fmt .Errorf ("too many arguments; migrate requires one directory argument (or stdin)" )
410
+ }
411
+ // Validate argument is a directory.
412
+ if len (args ) == 1 {
413
+ var err error
414
+ mr .dir , err = config .NormalizeDir (args [0 ])
415
+ if err != nil {
416
+ return err
417
+ }
418
+ }
419
+ // Store the stdin bytes if necessary so they can be used twice.
420
+ var stdinBytes []byte
421
+ var err error
422
+ if len (args ) == 0 {
423
+ stdinBytes , err = ioutil .ReadAll (reader )
424
+ if err != nil {
425
+ return err
426
+ }
427
+ if len (stdinBytes ) == 0 {
428
+ return fmt .Errorf ("no arguments means stdin has data; missing bytes on stdin" )
429
+ }
430
+ }
431
+
432
+ // Apply the ResourceGroup CRD to the cluster, ignoring if it already exists.
433
+ if err := mr .applyCRD (); err != nil {
434
+ return err
435
+ }
436
+
437
+ // Check if we need to migrate from ConfigMap to ResourceGroup.
438
+ if err := mr .migrateCMToRG (stdinBytes , args ); err != nil {
439
+ return err
440
+ }
441
+
442
+ // Migrate from Kptfile instead.
443
+ if mr .cmNotMigrated {
444
+ return mr .migrateKptfileToRG (args )
445
+ }
446
+
447
+ return nil
448
+ }
449
+
450
+ // migrateKptfileToRG extracts inventory information from a package's Kptfile
451
+ // into an external resourcegroup file.
452
+ func (mr * MigrateRunner ) migrateKptfileToRG (args []string ) error {
453
+ const op errors.Op = "migratecmd.migrateKptfileToRG"
454
+ klog .V (4 ).Infoln ("attempting to migrate from Kptfile inventory" )
455
+ fmt .Fprint (mr .ioStreams .Out , " reading existing Kptfile..." )
456
+ if ! mr .dryRun {
457
+ dir := args [0 ]
458
+ p , err := pkg .New (dir )
459
+ if err != nil {
460
+ return err
461
+ }
462
+ kf , err := p .Kptfile ()
463
+ if err != nil {
464
+ return err
465
+ }
466
+
467
+ if _ , err := kptfileutil .ValidateInventory (kf .Inventory ); err != nil {
468
+ // Invalid Kptfile.
469
+ return err
470
+ }
471
+
472
+ // Make sure resourcegroup file does not exist.
473
+ _ , rgFileErr := os .Stat (filepath .Join (dir , mr .rgFile ))
474
+ switch {
475
+ case rgFileErr == nil :
476
+ return errors .E (op , errors .IO , types .UniquePath (dir ), "the resourcegroup file already exists and inventory information cannot be migrated" )
477
+ case err != nil && ! goerrors .Is (err , os .ErrNotExist ):
478
+ return errors .E (op , errors .IO , types .UniquePath (dir ), err )
479
+ }
480
+
481
+ err = (& cmdliveinit.ConfigureInventoryInfo {
482
+ Pkg : p ,
483
+ Factory : mr .factory ,
484
+ Quiet : true ,
485
+ Name : kf .Inventory .Name ,
486
+ InventoryID : kf .Inventory .InventoryID ,
487
+ RGFileName : mr .rgFile ,
488
+ Force : true ,
489
+ }).Run (mr .ctx )
490
+
491
+ if err != nil {
492
+ return err
493
+ }
494
+ }
495
+ fmt .Fprint (mr .ioStreams .Out , "success\n " )
496
+ return nil
497
+ }
498
+
499
+ // migrateCMToRG migrates from ConfigMap to resourcegroup object.
500
+ func (mr * MigrateRunner ) migrateCMToRG (stdinBytes []byte , args []string ) error {
501
+ // Create the inventory clients for reading inventories based on RG and
502
+ // ConfigMap.
503
+ rgInvClient , err := mr .rgInvClientFunc (mr .factory )
504
+ if err != nil {
505
+ return err
506
+ }
507
+ cmInvClient , err := mr .cmInvClientFunc (mr .factory )
508
+ if err != nil {
509
+ return err
510
+ }
511
+ // Retrieve the current ConfigMap inventory objects.
512
+ cmInvObj , err := mr .retrieveConfigMapInv (bytes .NewReader (stdinBytes ), args )
513
+ if err != nil {
514
+ if _ , ok := err .(inventory.NoInventoryObjError ); ok {
515
+ // No ConfigMap inventory means the migration has already run before.
516
+ klog .V (4 ).Infoln ("swallowing no ConfigMap inventory error" )
517
+ mr .cmNotMigrated = true
518
+ return nil
519
+ }
520
+ klog .V (4 ).Infof ("error retrieving ConfigMap inventory object: %s" , err )
521
+ return err
522
+ }
523
+ cmInventoryID := cmInvObj .ID ()
524
+ klog .V (4 ).Infof ("previous inventoryID: %s" , cmInventoryID )
525
+ // Create ResourceGroup object file locallly (e.g. namespace, name, id).
526
+ if err := mr .createRGfile (mr .ctx , args , cmInventoryID ); err != nil {
527
+ return err
528
+ }
529
+ cmObjs , err := mr .retrieveInvObjs (cmInvClient , cmInvObj )
530
+ if err != nil {
531
+ return err
532
+ }
533
+ if len (cmObjs ) > 0 {
534
+ // Migrate the ConfigMap inventory objects to a ResourceGroup custom resource.
535
+ if err = mr .migrateObjs (rgInvClient , cmObjs , bytes .NewReader (stdinBytes ), args ); err != nil {
536
+ return err
537
+ }
538
+ // Delete the old ConfigMap inventory object.
539
+ if err = mr .deleteConfigMapInv (cmInvClient , cmInvObj ); err != nil {
540
+ return err
541
+ }
542
+ }
543
+ return mr .deleteConfigMapFile ()
544
+ }
545
+
546
+ // createRGfile writes the inventory information into the resourcegroup object.
547
+ func (mr * MigrateRunner ) createRGfile (ctx context.Context , args []string , prevID string ) error {
548
+ fmt .Fprint (mr .ioStreams .Out , " creating ResourceGroup object file..." )
549
+ if ! mr .dryRun {
550
+ p , err := pkg .New (args [0 ])
551
+ if err != nil {
552
+ return err
553
+ }
554
+ err = (& cmdliveinit.ConfigureInventoryInfo {
555
+ Pkg : p ,
556
+ Factory : mr .factory ,
557
+ Quiet : true ,
558
+ InventoryID : prevID ,
559
+ RGFileName : mr .rgFile ,
560
+ Force : mr .force ,
561
+ }).Run (ctx )
562
+
563
+ if err != nil {
564
+ var invExistsError * cmdliveinit.InvExistsError
565
+ if errors .As (err , & invExistsError ) {
566
+ fmt .Fprint (mr .ioStreams .Out , "values already exist..." )
567
+ } else {
568
+ return err
569
+ }
570
+ }
571
+ }
572
+ fmt .Fprint (mr .ioStreams .Out , "success\n " )
573
+ return nil
574
+ }
0 commit comments