github.com/mkimuram/operator-sdk@v0.7.1-0.20190410172100-52ad33a4bda0/internal/util/projutil/project_util.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 projutil
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"os/exec"
    21  	"path/filepath"
    22  	"regexp"
    23  	"strings"
    24  
    25  	log "github.com/sirupsen/logrus"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  const (
    30  	GopathEnv  = "GOPATH"
    31  	GoFlagsEnv = "GOFLAGS"
    32  	SrcDir     = "src"
    33  
    34  	fsep            = string(filepath.Separator)
    35  	mainFile        = "cmd" + fsep + "manager" + fsep + "main.go"
    36  	buildDockerfile = "build" + fsep + "Dockerfile"
    37  	rolesDir        = "roles"
    38  	helmChartsDir   = "helm-charts"
    39  )
    40  
    41  // OperatorType - the type of operator
    42  type OperatorType = string
    43  
    44  const (
    45  	// OperatorTypeGo - golang type of operator.
    46  	OperatorTypeGo OperatorType = "go"
    47  	// OperatorTypeAnsible - ansible type of operator.
    48  	OperatorTypeAnsible OperatorType = "ansible"
    49  	// OperatorTypeHelm - helm type of operator.
    50  	OperatorTypeHelm OperatorType = "helm"
    51  	// OperatorTypeUnknown - unknown type of operator.
    52  	OperatorTypeUnknown OperatorType = "unknown"
    53  )
    54  
    55  type ErrUnknownOperatorType struct {
    56  	Type string
    57  }
    58  
    59  func (e ErrUnknownOperatorType) Error() string {
    60  	if e.Type == "" {
    61  		return "unknown operator type"
    62  	}
    63  	return fmt.Sprintf(`unknown operator type "%v"`, e.Type)
    64  }
    65  
    66  // MustInProjectRoot checks if the current dir is the project root and returns
    67  // the current repo's import path, ex github.com/example-inc/app-operator
    68  func MustInProjectRoot() {
    69  	// If the current directory has a "build/dockerfile", then it is safe to say
    70  	// we are at the project root.
    71  	if _, err := os.Stat(buildDockerfile); err != nil {
    72  		if os.IsNotExist(err) {
    73  			log.Fatalf("Must run command in project root dir: project structure requires %s", buildDockerfile)
    74  		}
    75  		log.Fatalf("Error while checking if current directory is the project root: (%v)", err)
    76  	}
    77  }
    78  
    79  func CheckGoProjectCmd(cmd *cobra.Command) error {
    80  	if IsOperatorGo() {
    81  		return nil
    82  	}
    83  	return fmt.Errorf("'%s' can only be run for Go operators; %s does not exist.", cmd.CommandPath(), mainFile)
    84  }
    85  
    86  func MustGetwd() string {
    87  	wd, err := os.Getwd()
    88  	if err != nil {
    89  		log.Fatalf("Failed to get working directory: (%v)", err)
    90  	}
    91  	return wd
    92  }
    93  
    94  // CheckAndGetProjectGoPkg checks if this project's repository path is rooted under $GOPATH and returns the current directory's import path
    95  // e.g: "github.com/example-inc/app-operator"
    96  func CheckAndGetProjectGoPkg() string {
    97  	gopath := MustSetGopath(MustGetGopath())
    98  	goSrc := filepath.Join(gopath, SrcDir)
    99  	wd := MustGetwd()
   100  	currPkg := strings.Replace(wd, goSrc+fsep, "", 1)
   101  	// strip any "/" prefix from the repo path.
   102  	return strings.TrimPrefix(currPkg, fsep)
   103  }
   104  
   105  // GetOperatorType returns type of operator is in cwd.
   106  // This function should be called after verifying the user is in project root.
   107  func GetOperatorType() OperatorType {
   108  	switch {
   109  	case IsOperatorGo():
   110  		return OperatorTypeGo
   111  	case IsOperatorAnsible():
   112  		return OperatorTypeAnsible
   113  	case IsOperatorHelm():
   114  		return OperatorTypeHelm
   115  	}
   116  	return OperatorTypeUnknown
   117  }
   118  
   119  func IsOperatorGo() bool {
   120  	_, err := os.Stat(mainFile)
   121  	return err == nil
   122  }
   123  
   124  func IsOperatorAnsible() bool {
   125  	stat, err := os.Stat(rolesDir)
   126  	return err == nil && stat.IsDir()
   127  }
   128  
   129  func IsOperatorHelm() bool {
   130  	stat, err := os.Stat(helmChartsDir)
   131  	return err == nil && stat.IsDir()
   132  }
   133  
   134  // MustGetGopath gets GOPATH and ensures it is set and non-empty. If GOPATH
   135  // is not set or empty, MustGetGopath exits.
   136  func MustGetGopath() string {
   137  	gopath, ok := os.LookupEnv(GopathEnv)
   138  	if !ok || len(gopath) == 0 {
   139  		log.Fatal("GOPATH env not set")
   140  	}
   141  	return gopath
   142  }
   143  
   144  // MustSetGopath sets GOPATH=currentGopath after processing a path list,
   145  // if any, then returns the set path. If GOPATH cannot be set, MustSetGopath
   146  // exits.
   147  func MustSetGopath(currentGopath string) string {
   148  	var (
   149  		newGopath   string
   150  		cwdInGopath bool
   151  		wd          = MustGetwd()
   152  	)
   153  	for _, newGopath = range strings.Split(currentGopath, ":") {
   154  		if strings.HasPrefix(filepath.Dir(wd), newGopath) {
   155  			cwdInGopath = true
   156  			break
   157  		}
   158  	}
   159  	if !cwdInGopath {
   160  		log.Fatalf("Project not in $GOPATH")
   161  	}
   162  	if err := os.Setenv(GopathEnv, newGopath); err != nil {
   163  		log.Fatal(err)
   164  	}
   165  	return newGopath
   166  }
   167  
   168  func ExecCmd(cmd *exec.Cmd) error {
   169  	cmd.Stdout = os.Stdout
   170  	cmd.Stderr = os.Stderr
   171  	err := cmd.Run()
   172  	if err != nil {
   173  		return fmt.Errorf("failed to exec %#v: %v", cmd.Args, err)
   174  	}
   175  	return nil
   176  }
   177  
   178  var flagRe = regexp.MustCompile("(.* )?-v(.* )?")
   179  
   180  // IsGoVerbose returns true if GOFLAGS contains "-v". This function is useful
   181  // when deciding whether to make "go" command output verbose.
   182  func IsGoVerbose() bool {
   183  	gf, ok := os.LookupEnv(GoFlagsEnv)
   184  	return ok && len(gf) != 0 && flagRe.MatchString(gf)
   185  }