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

     1  // Copyright 2018 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 up
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"os/exec"
    21  	"os/signal"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  	"syscall"
    26  
    27  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    28  	k8sInternal "github.com/operator-framework/operator-sdk/internal/util/k8sutil"
    29  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    30  	"github.com/operator-framework/operator-sdk/pkg/ansible"
    31  	aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags"
    32  	"github.com/operator-framework/operator-sdk/pkg/helm"
    33  	hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags"
    34  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    35  	"github.com/operator-framework/operator-sdk/pkg/log/zap"
    36  	sdkVersion "github.com/operator-framework/operator-sdk/version"
    37  
    38  	log "github.com/sirupsen/logrus"
    39  	"github.com/spf13/cobra"
    40  	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
    41  )
    42  
    43  // newLocalCmd - up local command to run an operator loccally
    44  func newLocalCmd() *cobra.Command {
    45  	upLocalCmd := &cobra.Command{
    46  		Use:   "local",
    47  		Short: "Launches the operator locally",
    48  		Long: `The operator-sdk up local command launches the operator on the local machine
    49  by building the operator binary with the ability to access a
    50  kubernetes cluster using a kubeconfig file.
    51  `,
    52  		RunE: upLocalFunc,
    53  	}
    54  
    55  	upLocalCmd.Flags().StringVar(&kubeConfig, "kubeconfig", "", "The file path to kubernetes configuration file; defaults to location specified by $KUBECONFIG with a fallback to $HOME/.kube/config if not set")
    56  	upLocalCmd.Flags().StringVar(&operatorFlags, "operator-flags", "", "The flags that the operator needs. Example: \"--flag1 value1 --flag2=value2\"")
    57  	upLocalCmd.Flags().StringVar(&namespace, "namespace", "", "The namespace where the operator watches for changes.")
    58  	upLocalCmd.Flags().StringVar(&ldFlags, "go-ldflags", "", "Set Go linker options")
    59  	switch projutil.GetOperatorType() {
    60  	case projutil.OperatorTypeAnsible:
    61  		ansibleOperatorFlags = aoflags.AddTo(upLocalCmd.Flags(), "(ansible operator)")
    62  	case projutil.OperatorTypeHelm:
    63  		helmOperatorFlags = hoflags.AddTo(upLocalCmd.Flags(), "(helm operator)")
    64  	}
    65  	return upLocalCmd
    66  }
    67  
    68  var (
    69  	kubeConfig           string
    70  	operatorFlags        string
    71  	namespace            string
    72  	ldFlags              string
    73  	ansibleOperatorFlags *aoflags.AnsibleOperatorFlags
    74  	helmOperatorFlags    *hoflags.HelmOperatorFlags
    75  )
    76  
    77  func upLocalFunc(cmd *cobra.Command, args []string) error {
    78  	log.Info("Running the operator locally.")
    79  
    80  	// get default namespace to watch if unset
    81  	if !cmd.Flags().Changed("namespace") {
    82  		_, defaultNamespace, err := k8sInternal.GetKubeconfigAndNamespace(kubeConfig)
    83  		if err != nil {
    84  			return fmt.Errorf("failed to get kubeconfig and default namespace: %v", err)
    85  		}
    86  		namespace = defaultNamespace
    87  	}
    88  	log.Infof("Using namespace %s.", namespace)
    89  
    90  	switch t := projutil.GetOperatorType(); t {
    91  	case projutil.OperatorTypeGo:
    92  		return upLocal()
    93  	case projutil.OperatorTypeAnsible:
    94  		return upLocalAnsible()
    95  	case projutil.OperatorTypeHelm:
    96  		return upLocalHelm()
    97  	}
    98  	return projutil.ErrUnknownOperatorType{}
    99  }
   100  
   101  func upLocal() error {
   102  	projutil.MustInProjectRoot()
   103  	absProjectPath := projutil.MustGetwd()
   104  	projectName := filepath.Base(absProjectPath)
   105  	outputBinName := filepath.Join(scaffold.BuildBinDir, projectName+"-local")
   106  	if err := buildLocal(outputBinName); err != nil {
   107  		return fmt.Errorf("failed to build operator to run locally: (%v)", err)
   108  	}
   109  
   110  	args := []string{}
   111  	if operatorFlags != "" {
   112  		extraArgs := strings.Split(operatorFlags, " ")
   113  		args = append(args, extraArgs...)
   114  	}
   115  	dc := exec.Command(outputBinName, args...)
   116  	c := make(chan os.Signal)
   117  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   118  	go func() {
   119  		<-c
   120  		err := dc.Process.Kill()
   121  		if err != nil {
   122  			log.Fatalf("Failed to terminate the operator: (%v)", err)
   123  		}
   124  		os.Exit(0)
   125  	}()
   126  	dc.Env = os.Environ()
   127  	// only set env var if user explicitly specified a kubeconfig path
   128  	if kubeConfig != "" {
   129  		dc.Env = append(dc.Env, fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, kubeConfig))
   130  	}
   131  	dc.Env = append(dc.Env, fmt.Sprintf("%v=%v", k8sutil.WatchNamespaceEnvVar, namespace))
   132  	if err := projutil.ExecCmd(dc); err != nil {
   133  		return fmt.Errorf("failed to run operator locally: (%v)", err)
   134  	}
   135  	return nil
   136  }
   137  
   138  func upLocalAnsible() error {
   139  	logf.SetLogger(zap.Logger())
   140  	if err := setupOperatorEnv(); err != nil {
   141  		return err
   142  	}
   143  	return ansible.Run(ansibleOperatorFlags)
   144  }
   145  
   146  func upLocalHelm() error {
   147  	logf.SetLogger(zap.Logger())
   148  	if err := setupOperatorEnv(); err != nil {
   149  		return err
   150  	}
   151  	return helm.Run(helmOperatorFlags)
   152  }
   153  
   154  func setupOperatorEnv() error {
   155  	// Set the kubeconfig that the manager will be able to grab
   156  	// only set env var if user explicitly specified a kubeconfig path
   157  	if kubeConfig != "" {
   158  		if err := os.Setenv(k8sutil.KubeConfigEnvVar, kubeConfig); err != nil {
   159  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.KubeConfigEnvVar, err)
   160  		}
   161  	}
   162  	// Set the namespace that the manager will be able to grab
   163  	if namespace != "" {
   164  		if err := os.Setenv(k8sutil.WatchNamespaceEnvVar, namespace); err != nil {
   165  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.WatchNamespaceEnvVar, err)
   166  		}
   167  	}
   168  	// Set the operator name, if not already set
   169  	projutil.MustInProjectRoot()
   170  	if _, err := k8sutil.GetOperatorName(); err != nil {
   171  		operatorName := filepath.Base(projutil.MustGetwd())
   172  		if err := os.Setenv(k8sutil.OperatorNameEnvVar, operatorName); err != nil {
   173  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.OperatorNameEnvVar, err)
   174  		}
   175  	}
   176  	return nil
   177  }
   178  
   179  func buildLocal(outputBinName string) error {
   180  	var args []string
   181  	if ldFlags != "" {
   182  		args = []string{"-ldflags", ldFlags}
   183  	}
   184  	opts := projutil.GoCmdOptions{
   185  		BinName:     outputBinName,
   186  		PackagePath: filepath.Join(projutil.CheckAndGetProjectGoPkg(), scaffold.ManagerDir),
   187  		Args:        args,
   188  		GoMod:       projutil.IsDepManagerGoMod(),
   189  	}
   190  	return projutil.GoBuild(opts)
   191  }
   192  
   193  func printVersion() {
   194  	log.Infof("Go Version: %s", runtime.Version())
   195  	log.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)
   196  	log.Infof("Version of operator-sdk: %v", sdkVersion.Version)
   197  }