github.skymusic.top/operator-framework/operator-sdk@v0.8.2/pkg/test/main_entry.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 test
    16  
    17  import (
    18  	"bytes"
    19  	"flag"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"os/exec"
    24  	"os/signal"
    25  	"path/filepath"
    26  	"syscall"
    27  	"testing"
    28  
    29  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    30  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    31  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    32  
    33  	log "github.com/sirupsen/logrus"
    34  	"k8s.io/client-go/tools/clientcmd"
    35  )
    36  
    37  const (
    38  	ProjRootFlag          = "root"
    39  	KubeConfigFlag        = "kubeconfig"
    40  	NamespacedManPathFlag = "namespacedMan"
    41  	GlobalManPathFlag     = "globalMan"
    42  	SingleNamespaceFlag   = "singleNamespace"
    43  	TestNamespaceEnv      = "TEST_NAMESPACE"
    44  	LocalOperatorFlag     = "localOperator"
    45  )
    46  
    47  func MainEntry(m *testing.M) {
    48  	projRoot := flag.String(ProjRootFlag, "", "path to project root")
    49  	kubeconfigPath := flag.String(KubeConfigFlag, "", "path to kubeconfig")
    50  	globalManPath := flag.String(GlobalManPathFlag, "", "path to operator manifest")
    51  	namespacedManPath := flag.String(NamespacedManPathFlag, "", "path to rbac manifest")
    52  	singleNamespace = flag.Bool(SingleNamespaceFlag, false, "enable single namespace mode")
    53  	localOperator := flag.Bool(LocalOperatorFlag, false, "enable if operator is running locally (not in cluster)")
    54  	flag.Parse()
    55  	// go test always runs from the test directory; change to project root
    56  	err := os.Chdir(*projRoot)
    57  	if err != nil {
    58  		log.Fatalf("Failed to change directory to project root: %v", err)
    59  	}
    60  	if err := setup(kubeconfigPath, namespacedManPath, *localOperator); err != nil {
    61  		log.Fatalf("Failed to set up framework: %v", err)
    62  	}
    63  	// setup local operator command, but don't start it yet
    64  	var localCmd *exec.Cmd
    65  	var localCmdOutBuf, localCmdErrBuf bytes.Buffer
    66  	if *localOperator {
    67  		projectName := filepath.Base(projutil.MustGetwd())
    68  		outputBinName := filepath.Join(scaffold.BuildBinDir, projectName+"-local")
    69  		opts := projutil.GoCmdOptions{
    70  			BinName:     outputBinName,
    71  			PackagePath: filepath.Join(scaffold.ManagerDir, scaffold.CmdFile),
    72  			GoMod:       projutil.IsDepManagerGoMod(),
    73  		}
    74  		if err := projutil.GoBuild(opts); err != nil {
    75  			log.Fatalf("Failed to build local operator binary: %s", err)
    76  		}
    77  		localCmd = exec.Command(outputBinName)
    78  		localCmd.Stdout = &localCmdOutBuf
    79  		localCmd.Stderr = &localCmdErrBuf
    80  		c := make(chan os.Signal)
    81  		signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    82  		go func() {
    83  			<-c
    84  			err := localCmd.Process.Signal(os.Interrupt)
    85  			if err != nil {
    86  				log.Fatalf("Failed to terminate the operator: (%v)", err)
    87  			}
    88  			os.Exit(0)
    89  		}()
    90  		if *kubeconfigPath != "" {
    91  			localCmd.Env = append(os.Environ(), fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, *kubeconfigPath))
    92  		} else {
    93  			// we can hardcode index 0 as that is the highest priority kubeconfig to be loaded and will always
    94  			// be populated by NewDefaultClientConfigLoadingRules()
    95  			localCmd.Env = append(os.Environ(), fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, clientcmd.NewDefaultClientConfigLoadingRules().Precedence[0]))
    96  		}
    97  		localCmd.Env = append(localCmd.Env, fmt.Sprintf("%v=%v", k8sutil.WatchNamespaceEnvVar, Global.Namespace))
    98  	}
    99  	// setup context to use when setting up crd
   100  	ctx := NewTestCtx(nil)
   101  	// os.Exit stops the program before the deferred functions run
   102  	// to fix this, we put the exit in the defer as well
   103  	defer func() {
   104  		// start local operator before running tests
   105  		if *localOperator {
   106  			err := localCmd.Start()
   107  			if err != nil {
   108  				log.Fatalf("Failed to run operator locally: (%v)", err)
   109  			}
   110  			log.Info("Started local operator")
   111  		}
   112  		exitCode := m.Run()
   113  		if *localOperator {
   114  			err := localCmd.Process.Kill()
   115  			if err != nil {
   116  				log.Warn("Failed to stop local operator process")
   117  			}
   118  			log.Infof("Local operator stdout: %s", string(localCmdOutBuf.Bytes()))
   119  			log.Infof("Local operator stderr: %s", string(localCmdErrBuf.Bytes()))
   120  		}
   121  		ctx.Cleanup()
   122  		os.Exit(exitCode)
   123  	}()
   124  	// create crd
   125  	globalYAML, err := ioutil.ReadFile(*globalManPath)
   126  	if err != nil {
   127  		log.Fatalf("Failed to read global resource manifest: %v", err)
   128  	}
   129  	err = ctx.createFromYAML(globalYAML, true, &CleanupOptions{TestContext: ctx})
   130  	if err != nil {
   131  		log.Fatalf("Failed to create resource(s) in global resource manifest: %v", err)
   132  	}
   133  }