github.com/jmrodri/operator-sdk@v0.5.0/commands/operator-sdk/cmd/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  	k8sInternal "github.com/operator-framework/operator-sdk/internal/util/k8sutil"
    28  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    29  	"github.com/operator-framework/operator-sdk/pkg/ansible"
    30  	aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags"
    31  	"github.com/operator-framework/operator-sdk/pkg/helm"
    32  	hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags"
    33  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    34  	"github.com/operator-framework/operator-sdk/pkg/log/zap"
    35  	"github.com/operator-framework/operator-sdk/pkg/scaffold"
    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  	t := projutil.GetOperatorType()
    91  	switch t {
    92  	case projutil.OperatorTypeGo:
    93  		return upLocal()
    94  	case projutil.OperatorTypeAnsible:
    95  		return upLocalAnsible()
    96  	case projutil.OperatorTypeHelm:
    97  		return upLocalHelm()
    98  	}
    99  	return fmt.Errorf("unknown operator type '%v'", t)
   100  }
   101  
   102  func upLocal() error {
   103  	projutil.MustInProjectRoot()
   104  	absProjectPath := projutil.MustGetwd()
   105  	projectName := filepath.Base(absProjectPath)
   106  	outputBinName := filepath.Join(scaffold.BuildBinDir, projectName+"-local")
   107  	if err := buildLocal(outputBinName); err != nil {
   108  		return fmt.Errorf("failed to build operator to run locally: (%v)", err)
   109  	}
   110  
   111  	args := []string{}
   112  	if operatorFlags != "" {
   113  		extraArgs := strings.Split(operatorFlags, " ")
   114  		args = append(args, extraArgs...)
   115  	}
   116  	dc := exec.Command(outputBinName, args...)
   117  	c := make(chan os.Signal)
   118  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   119  	go func() {
   120  		<-c
   121  		err := dc.Process.Kill()
   122  		if err != nil {
   123  			log.Fatalf("Failed to terminate the operator: (%v)", err)
   124  		}
   125  		os.Exit(0)
   126  	}()
   127  	dc.Env = os.Environ()
   128  	// only set env var if user explicitly specified a kubeconfig path
   129  	if kubeConfig != "" {
   130  		dc.Env = append(dc.Env, fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, kubeConfig))
   131  	}
   132  	dc.Env = append(dc.Env, fmt.Sprintf("%v=%v", k8sutil.WatchNamespaceEnvVar, namespace))
   133  	if err := projutil.ExecCmd(dc); err != nil {
   134  		return fmt.Errorf("failed to run operator locally: (%v)", err)
   135  	}
   136  	return nil
   137  }
   138  
   139  func upLocalAnsible() error {
   140  	logf.SetLogger(zap.Logger())
   141  	if err := setupOperatorEnv(); err != nil {
   142  		return err
   143  	}
   144  	return ansible.Run(ansibleOperatorFlags)
   145  }
   146  
   147  func upLocalHelm() error {
   148  	logf.SetLogger(zap.Logger())
   149  	if err := setupOperatorEnv(); err != nil {
   150  		return err
   151  	}
   152  	return helm.Run(helmOperatorFlags)
   153  }
   154  
   155  func setupOperatorEnv() error {
   156  	// Set the kubeconfig that the manager will be able to grab
   157  	// only set env var if user explicitly specified a kubeconfig path
   158  	if kubeConfig != "" {
   159  		if err := os.Setenv(k8sutil.KubeConfigEnvVar, kubeConfig); err != nil {
   160  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.KubeConfigEnvVar, err)
   161  		}
   162  	}
   163  	// Set the namespace that the manager will be able to grab
   164  	if namespace != "" {
   165  		if err := os.Setenv(k8sutil.WatchNamespaceEnvVar, namespace); err != nil {
   166  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.WatchNamespaceEnvVar, err)
   167  		}
   168  	}
   169  	// Set the operator name, if not already set
   170  	projutil.MustInProjectRoot()
   171  	if _, err := k8sutil.GetOperatorName(); err != nil {
   172  		operatorName := filepath.Base(projutil.MustGetwd())
   173  		if err := os.Setenv(k8sutil.OperatorNameEnvVar, operatorName); err != nil {
   174  			return fmt.Errorf("failed to set %s environment variable: (%v)", k8sutil.OperatorNameEnvVar, err)
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  func buildLocal(outputBinName string) error {
   181  	args := []string{"build", "-o", outputBinName}
   182  	if ldFlags != "" {
   183  		args = append(args, "-ldflags", ldFlags)
   184  	}
   185  	args = append(args, filepath.Join(scaffold.ManagerDir, scaffold.CmdFile))
   186  
   187  	bc := exec.Command("go", args...)
   188  	if err := projutil.ExecCmd(bc); err != nil {
   189  		return err
   190  	}
   191  	return nil
   192  }
   193  
   194  func printVersion() {
   195  	log.Infof("Go Version: %s", runtime.Version())
   196  	log.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)
   197  	log.Infof("Version of operator-sdk: %v", sdkVersion.Version)
   198  }