github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/builder/tools/migrate_scripts.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package tools
    21  
    22  import (
    23  	"fmt"
    24  	"io"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	"github.com/spf13/cobra"
    30  	corev1 "k8s.io/api/core/v1"
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    33  	"k8s.io/kubectl/pkg/util/templates"
    34  	"sigs.k8s.io/controller-runtime/pkg/client"
    35  
    36  	"github.com/1aal/kubeblocks/pkg/cli/cmd/builder/template"
    37  	"github.com/1aal/kubeblocks/pkg/cli/printer"
    38  	"github.com/1aal/kubeblocks/pkg/cli/util"
    39  	cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core"
    40  	cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util"
    41  	"github.com/1aal/kubeblocks/pkg/generics"
    42  )
    43  
    44  type migrateOptions struct {
    45  	genericiooptions.IOStreams
    46  
    47  	Factory cmdutil.Factory
    48  	// dynamic dynamic.Interface
    49  
    50  	helmTemplateDir   string
    51  	scriptsOutputPath string
    52  	regex             string
    53  	cmName            string
    54  	overwrite         bool
    55  }
    56  
    57  func (o *migrateOptions) complete() error {
    58  	if o.helmTemplateDir == "" {
    59  		return cfgcore.MakeError("helm template dir is required")
    60  	}
    61  	if ok, _ := cfgutil.CheckPathExists(o.helmTemplateDir); !ok {
    62  		return cfgcore.MakeError("helm template dir is not exists")
    63  	}
    64  	if o.regex == "" && o.cmName == "" {
    65  		return cfgcore.MakeError("regex or cm name are required")
    66  	}
    67  
    68  	if o.scriptsOutputPath == "" {
    69  		return cfgcore.MakeError("scripts output path is required")
    70  	}
    71  
    72  	ok, err := cfgutil.CheckPathExists(o.scriptsOutputPath)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	if !ok {
    77  		err = os.MkdirAll(o.scriptsOutputPath, os.ModePerm)
    78  	}
    79  	return err
    80  }
    81  
    82  func (o *migrateOptions) run() error {
    83  	tmpDir, err := os.MkdirTemp(os.TempDir(), "tmp-")
    84  	if err != nil {
    85  		return err
    86  	}
    87  	defer os.RemoveAll(tmpDir)
    88  
    89  	output := filepath.Join(tmpDir, "helm-output")
    90  	if err := template.HelmTemplate(o.helmTemplateDir, output); err != nil {
    91  		return err
    92  	}
    93  
    94  	allObjects, err := template.CreateObjectsFromDirectory(output)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	if o.cmName != "" {
   100  		return o.processSpecConfigMap(allObjects)
   101  	}
   102  	_ = template.GetTypedResourceObjectBySignature(allObjects, generics.ConfigMapSignature, o.processConfigMap)
   103  	return nil
   104  }
   105  
   106  func (o *migrateOptions) processSpecConfigMap(objects []client.Object) error {
   107  	cm := template.GetTypedResourceObjectBySignature(objects, generics.ConfigMapSignature, template.WithResourceName(o.cmName))
   108  	if cm == nil {
   109  		return cfgcore.MakeError("configmap %s not found", o.cmName)
   110  	}
   111  	dumpHelmScripts(cm.Data, o.scriptsOutputPath, o.Out, o.overwrite)
   112  	return nil
   113  }
   114  
   115  func (o *migrateOptions) processConfigMap(object client.Object) (r bool) {
   116  	r = false
   117  	cm, ok := object.(*corev1.ConfigMap)
   118  	if !ok {
   119  		return
   120  	}
   121  	if strings.Contains(cm.Name, "script") {
   122  		dumpHelmScripts(cm.Data, o.scriptsOutputPath, o.Out, o.overwrite)
   123  	}
   124  	return
   125  }
   126  
   127  func dumpHelmScripts(data map[string]string, outputPath string, out io.Writer, overwrite bool) {
   128  	if outputPath == "" {
   129  		return
   130  	}
   131  	for k, v := range data {
   132  		f := filepath.Join(outputPath, k)
   133  		if ok, _ := cfgutil.CheckPathExists(f); ok {
   134  			fmt.Fprintf(out, "file [%s] is exists\n", printer.BoldRed(k))
   135  			if !overwrite {
   136  				os.Exit(-1)
   137  			}
   138  		}
   139  		util.CheckErr(os.WriteFile(f, []byte(v), os.ModePerm))
   140  	}
   141  }
   142  
   143  func (o *migrateOptions) buildFlags(cmd *cobra.Command) {
   144  	cmd.Flags().StringVar(&o.helmTemplateDir, "helm", "", "specify the helm template dir")
   145  	cmd.Flags().StringVar(&o.scriptsOutputPath, "output", "", "specify the scripts output path")
   146  	cmd.Flags().StringVar(&o.cmName, "configmap", "", "specify the configmap name")
   147  	cmd.Flags().StringVar(&o.regex, "regex", "", "specify the regex for configmap")
   148  	cmd.Flags().BoolVar(&o.overwrite, "force", false, "whether overwrite the exists file")
   149  }
   150  
   151  var migrateExamples = templates.Examples(` 
   152  `)
   153  
   154  func NewMigrateHelmScriptsCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   155  	o := &migrateOptions{
   156  		Factory:   f,
   157  		IOStreams: streams,
   158  	}
   159  	cmd := &cobra.Command{
   160  		Use:     "migrate-scripts",
   161  		Aliases: []string{"migrate"},
   162  		Short:   "migrate - a developer tool.",
   163  		Example: migrateExamples,
   164  		Run: func(cmd *cobra.Command, args []string) {
   165  			util.CheckErr(o.complete())
   166  			util.CheckErr(o.run())
   167  		},
   168  	}
   169  	o.buildFlags(cmd)
   170  	return cmd
   171  }