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