github.skymusic.top/operator-framework/operator-sdk@v0.8.2/cmd/operator-sdk/migrate/cmd.go (about)

     1  // Copyright 2019 The Operator-SDK Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package migrate
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    23  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/ansible"
    24  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/helm"
    25  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input"
    26  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    27  
    28  	"github.com/pkg/errors"
    29  	log "github.com/sirupsen/logrus"
    30  	"github.com/spf13/cobra"
    31  )
    32  
    33  var (
    34  	depManager string
    35  	headerFile string
    36  )
    37  
    38  // NewCmd returns a command that will add source code to an existing non-go operator
    39  func NewCmd() *cobra.Command {
    40  	newCmd := &cobra.Command{
    41  		Use:   "migrate",
    42  		Short: "Adds source code to an operator",
    43  		Long:  `operator-sdk migrate adds a main.go source file and any associated source files for an operator that is not of the "go" type.`,
    44  		RunE:  migrateRun,
    45  	}
    46  
    47  	newCmd.Flags().StringVar(&depManager, "dep-manager", "modules", `Dependency manager the new project will use (choices: "dep", "modules")`)
    48  	newCmd.Flags().StringVar(&headerFile, "header-file", "", "Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt")
    49  
    50  	return newCmd
    51  }
    52  
    53  // migrateRun determines the current operator type and runs the corresponding
    54  // migrate function.
    55  func migrateRun(cmd *cobra.Command, args []string) error {
    56  	projutil.MustInProjectRoot()
    57  
    58  	_ = projutil.CheckAndGetProjectGoPkg()
    59  
    60  	opType := projutil.GetOperatorType()
    61  	switch opType {
    62  	case projutil.OperatorTypeAnsible:
    63  		return migrateAnsible()
    64  	case projutil.OperatorTypeHelm:
    65  		return migrateHelm()
    66  	}
    67  	return fmt.Errorf("operator of type %s cannot be migrated", opType)
    68  }
    69  
    70  // migrateAnsible runs the migration process for an ansible-based operator
    71  func migrateAnsible() error {
    72  	wd := projutil.MustGetwd()
    73  
    74  	cfg := &input.Config{
    75  		Repo:           projutil.CheckAndGetProjectGoPkg(),
    76  		AbsProjectPath: wd,
    77  		ProjectName:    filepath.Base(wd),
    78  	}
    79  
    80  	dockerfile := ansible.DockerfileHybrid{
    81  		Watches: true,
    82  		Roles:   true,
    83  	}
    84  	_, err := os.Stat(ansible.PlaybookYamlFile)
    85  	switch {
    86  	case err == nil:
    87  		dockerfile.Playbook = true
    88  	case os.IsNotExist(err):
    89  		log.Info("No playbook was found, so not including it in the new Dockerfile")
    90  	default:
    91  		return fmt.Errorf("error trying to stat %s: (%v)", ansible.PlaybookYamlFile, err)
    92  	}
    93  	if err := renameDockerfile(); err != nil {
    94  		return err
    95  	}
    96  
    97  	s := &scaffold.Scaffold{}
    98  	if headerFile != "" {
    99  		err = s.Execute(cfg, &scaffold.Boilerplate{BoilerplateSrcPath: headerFile})
   100  		if err != nil {
   101  			return fmt.Errorf("boilerplate scaffold failed: (%v)", err)
   102  		}
   103  		s.BoilerplatePath = headerFile
   104  	}
   105  
   106  	if err := scaffoldAnsibleDepManager(s, cfg); err != nil {
   107  		return errors.Wrap(err, "migrate Ansible dependency manager file scaffold failed")
   108  	}
   109  
   110  	err = s.Execute(cfg,
   111  		&ansible.Main{},
   112  		&dockerfile,
   113  		&ansible.Entrypoint{},
   114  		&ansible.UserSetup{},
   115  		&ansible.K8sStatus{},
   116  		&ansible.AoLogs{},
   117  	)
   118  	if err != nil {
   119  		return fmt.Errorf("migrate ansible scaffold failed: (%v)", err)
   120  	}
   121  	return nil
   122  }
   123  
   124  // migrateHelm runs the migration process for a helm-based operator
   125  func migrateHelm() error {
   126  	wd := projutil.MustGetwd()
   127  
   128  	cfg := &input.Config{
   129  		Repo:           projutil.CheckAndGetProjectGoPkg(),
   130  		AbsProjectPath: wd,
   131  		ProjectName:    filepath.Base(wd),
   132  	}
   133  
   134  	if err := renameDockerfile(); err != nil {
   135  		return err
   136  	}
   137  
   138  	s := &scaffold.Scaffold{}
   139  	if headerFile != "" {
   140  		err := s.Execute(cfg, &scaffold.Boilerplate{BoilerplateSrcPath: headerFile})
   141  		if err != nil {
   142  			return fmt.Errorf("boilerplate scaffold failed: (%v)", err)
   143  		}
   144  		s.BoilerplatePath = headerFile
   145  	}
   146  
   147  	if err := scaffoldHelmDepManager(s, cfg); err != nil {
   148  		return errors.Wrap(err, "migrate Helm dependency manager file scaffold failed")
   149  	}
   150  
   151  	err := s.Execute(cfg,
   152  		&helm.Main{},
   153  		&helm.DockerfileHybrid{
   154  			Watches:    true,
   155  			HelmCharts: true,
   156  		},
   157  		&helm.Entrypoint{},
   158  		&helm.UserSetup{},
   159  	)
   160  	if err != nil {
   161  		return fmt.Errorf("migrate helm scaffold failed: (%v)", err)
   162  	}
   163  	return nil
   164  }
   165  
   166  func renameDockerfile() error {
   167  	dockerfilePath := filepath.Join(scaffold.BuildDir, scaffold.DockerfileFile)
   168  	newDockerfilePath := dockerfilePath + ".sdkold"
   169  	err := os.Rename(dockerfilePath, newDockerfilePath)
   170  	if err != nil {
   171  		return fmt.Errorf("failed to rename Dockerfile: (%v)", err)
   172  	}
   173  	log.Infof("Renamed Dockerfile to %s and replaced with newer version. Compare the new Dockerfile to your old one and manually migrate any customizations", newDockerfilePath)
   174  	return nil
   175  }
   176  
   177  func scaffoldHelmDepManager(s *scaffold.Scaffold, cfg *input.Config) error {
   178  	var files []input.File
   179  	switch m := projutil.DepManagerType(depManager); m {
   180  	case projutil.DepManagerDep:
   181  		files = append(files, &helm.GopkgToml{})
   182  	case projutil.DepManagerGoMod:
   183  		if err := goModCheck(); err != nil {
   184  			return err
   185  		}
   186  		files = append(files, &helm.GoMod{}, &scaffold.Tools{})
   187  	default:
   188  		return projutil.ErrInvalidDepManager(depManager)
   189  	}
   190  	return s.Execute(cfg, files...)
   191  }
   192  
   193  func scaffoldAnsibleDepManager(s *scaffold.Scaffold, cfg *input.Config) error {
   194  	var files []input.File
   195  	switch m := projutil.DepManagerType(depManager); m {
   196  	case projutil.DepManagerDep:
   197  		files = append(files, &ansible.GopkgToml{})
   198  	case projutil.DepManagerGoMod:
   199  		if err := goModCheck(); err != nil {
   200  			return err
   201  		}
   202  		files = append(files, &ansible.GoMod{}, &scaffold.Tools{})
   203  	default:
   204  		return projutil.ErrInvalidDepManager(depManager)
   205  	}
   206  	return s.Execute(cfg, files...)
   207  }
   208  
   209  func goModCheck() error {
   210  	goModOn, err := projutil.GoModOn()
   211  	if err == nil && !goModOn {
   212  		log.Fatal(`Dependency manager "modules" has been selected but go modules are not active. ` +
   213  			`Activate modules then run "operator-sdk migrate".`)
   214  	}
   215  	return err
   216  }