github.com/mkimuram/operator-sdk@v0.7.1-0.20190410172100-52ad33a4bda0/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  	"k8s.io/client-go/tools/clientcmd"
    30  
    31  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    32  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    33  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    34  	log "github.com/sirupsen/logrus"
    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  		absProjectPath := projutil.MustGetwd()
    68  		projectName := filepath.Base(absProjectPath)
    69  		outputBinName := filepath.Join(scaffold.BuildBinDir, projectName+"-local")
    70  		args := []string{"build", "-o", outputBinName}
    71  		args = append(args, filepath.Join(scaffold.ManagerDir, scaffold.CmdFile))
    72  		bc := exec.Command("go", args...)
    73  		if err := projutil.ExecCmd(bc); err != nil {
    74  			log.Fatalf("Failed to build local operator binary: %s", err)
    75  		}
    76  		localCmd = exec.Command(outputBinName)
    77  		localCmd.Stdout = &localCmdOutBuf
    78  		localCmd.Stderr = &localCmdErrBuf
    79  		c := make(chan os.Signal)
    80  		signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    81  		go func() {
    82  			<-c
    83  			err := localCmd.Process.Signal(os.Interrupt)
    84  			if err != nil {
    85  				log.Fatalf("Failed to terminate the operator: (%v)", err)
    86  			}
    87  			os.Exit(0)
    88  		}()
    89  		if *kubeconfigPath != "" {
    90  			localCmd.Env = append(os.Environ(), fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, *kubeconfigPath))
    91  		} else {
    92  			// we can hardcode index 0 as that is the highest priority kubeconfig to be loaded and will always
    93  			// be populated by NewDefaultClientConfigLoadingRules()
    94  			localCmd.Env = append(os.Environ(), fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, clientcmd.NewDefaultClientConfigLoadingRules().Precedence[0]))
    95  		}
    96  		localCmd.Env = append(localCmd.Env, fmt.Sprintf("%v=%v", k8sutil.WatchNamespaceEnvVar, Global.Namespace))
    97  	}
    98  	// setup context to use when setting up crd
    99  	ctx := NewTestCtx(nil)
   100  	// os.Exit stops the program before the deferred functions run
   101  	// to fix this, we put the exit in the defer as well
   102  	defer func() {
   103  		// start local operator before running tests
   104  		if *localOperator {
   105  			err := localCmd.Start()
   106  			if err != nil {
   107  				log.Fatalf("Failed to run operator locally: (%v)", err)
   108  			}
   109  			log.Info("Started local operator")
   110  		}
   111  		exitCode := m.Run()
   112  		if *localOperator {
   113  			err := localCmd.Process.Kill()
   114  			if err != nil {
   115  				log.Warn("Failed to stop local operator process")
   116  			}
   117  			log.Infof("Local operator stdout: %s", string(localCmdOutBuf.Bytes()))
   118  			log.Infof("Local operator stderr: %s", string(localCmdErrBuf.Bytes()))
   119  		}
   120  		ctx.Cleanup()
   121  		os.Exit(exitCode)
   122  	}()
   123  	// create crd
   124  	if *kubeconfigPath != "incluster" {
   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  	}
   134  }