github.com/joelanford/operator-sdk@v0.8.2/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 "path/filepath" 21 "regexp" 22 "strings" 23 24 homedir "github.com/mitchellh/go-homedir" 25 log "github.com/sirupsen/logrus" 26 "github.com/spf13/cobra" 27 ) 28 29 const ( 30 GoPathEnv = "GOPATH" 31 GoFlagsEnv = "GOFLAGS" 32 GoModEnv = "GO111MODULE" 33 SrcDir = "src" 34 35 fsep = string(filepath.Separator) 36 mainFile = "cmd" + fsep + "manager" + fsep + "main.go" 37 buildDockerfile = "build" + fsep + "Dockerfile" 38 rolesDir = "roles" 39 helmChartsDir = "helm-charts" 40 goModFile = "go.mod" 41 gopkgTOMLFile = "Gopkg.toml" 42 ) 43 44 // OperatorType - the type of operator 45 type OperatorType = string 46 47 const ( 48 // OperatorTypeGo - golang type of operator. 49 OperatorTypeGo OperatorType = "go" 50 // OperatorTypeAnsible - ansible type of operator. 51 OperatorTypeAnsible OperatorType = "ansible" 52 // OperatorTypeHelm - helm type of operator. 53 OperatorTypeHelm OperatorType = "helm" 54 // OperatorTypeUnknown - unknown type of operator. 55 OperatorTypeUnknown OperatorType = "unknown" 56 ) 57 58 type ErrUnknownOperatorType struct { 59 Type string 60 } 61 62 func (e ErrUnknownOperatorType) Error() string { 63 if e.Type == "" { 64 return "unknown operator type" 65 } 66 return fmt.Sprintf(`unknown operator type "%v"`, e.Type) 67 } 68 69 type DepManagerType string 70 71 const ( 72 DepManagerGoMod DepManagerType = "modules" 73 DepManagerDep DepManagerType = "dep" 74 ) 75 76 type ErrInvalidDepManager string 77 78 func (e ErrInvalidDepManager) Error() string { 79 return fmt.Sprintf(`"%s" is not a valid dep manager; dep manager must be one of ["%v", "%v"]`, e, DepManagerDep, DepManagerGoMod) 80 } 81 82 var ErrNoDepManager = fmt.Errorf(`no valid dependency manager file found; dep manager must be one of ["%v", "%v"]`, DepManagerDep, DepManagerGoMod) 83 84 func GetDepManagerType() (DepManagerType, error) { 85 if IsDepManagerDep() { 86 return DepManagerDep, nil 87 } else if IsDepManagerGoMod() { 88 return DepManagerGoMod, nil 89 } 90 return "", ErrNoDepManager 91 } 92 93 func IsDepManagerDep() bool { 94 _, err := os.Stat(gopkgTOMLFile) 95 return err == nil || os.IsExist(err) 96 } 97 98 func IsDepManagerGoMod() bool { 99 _, err := os.Stat(goModFile) 100 return err == nil || os.IsExist(err) 101 } 102 103 // MustInProjectRoot checks if the current dir is the project root and returns 104 // the current repo's import path, ex github.com/example-inc/app-operator 105 func MustInProjectRoot() { 106 // If the current directory has a "build/dockerfile", then it is safe to say 107 // we are at the project root. 108 if _, err := os.Stat(buildDockerfile); err != nil { 109 if os.IsNotExist(err) { 110 log.Fatalf("Must run command in project root dir: project structure requires %s", buildDockerfile) 111 } 112 log.Fatalf("Error while checking if current directory is the project root: (%v)", err) 113 } 114 } 115 116 func CheckGoProjectCmd(cmd *cobra.Command) error { 117 if IsOperatorGo() { 118 return nil 119 } 120 return fmt.Errorf("'%s' can only be run for Go operators; %s does not exist.", cmd.CommandPath(), mainFile) 121 } 122 123 func MustGetwd() string { 124 wd, err := os.Getwd() 125 if err != nil { 126 log.Fatalf("Failed to get working directory: (%v)", err) 127 } 128 return wd 129 } 130 131 func getHomeDir() (string, error) { 132 hd, err := homedir.Dir() 133 if err != nil { 134 return "", err 135 } 136 return homedir.Expand(hd) 137 } 138 139 // CheckAndGetProjectGoPkg checks if this project's repository path is rooted under $GOPATH and returns the current directory's import path 140 // e.g: "github.com/example-inc/app-operator" 141 func CheckAndGetProjectGoPkg() string { 142 gopath := MustSetGopath(MustGetGopath()) 143 goSrc := filepath.Join(gopath, SrcDir) 144 wd := MustGetwd() 145 currPkg := strings.Replace(wd, goSrc, "", 1) 146 // strip any "/" prefix from the repo path. 147 return strings.TrimPrefix(currPkg, fsep) 148 } 149 150 // GetOperatorType returns type of operator is in cwd. 151 // This function should be called after verifying the user is in project root. 152 func GetOperatorType() OperatorType { 153 switch { 154 case IsOperatorGo(): 155 return OperatorTypeGo 156 case IsOperatorAnsible(): 157 return OperatorTypeAnsible 158 case IsOperatorHelm(): 159 return OperatorTypeHelm 160 } 161 return OperatorTypeUnknown 162 } 163 164 func IsOperatorGo() bool { 165 _, err := os.Stat(mainFile) 166 return err == nil 167 } 168 169 func IsOperatorAnsible() bool { 170 stat, err := os.Stat(rolesDir) 171 return err == nil && stat.IsDir() 172 } 173 174 func IsOperatorHelm() bool { 175 stat, err := os.Stat(helmChartsDir) 176 return err == nil && stat.IsDir() 177 } 178 179 // MustGetGopath gets GOPATH and ensures it is set and non-empty. If GOPATH 180 // is not set or empty, MustGetGopath exits. 181 func MustGetGopath() string { 182 gopath, ok := os.LookupEnv(GoPathEnv) 183 if !ok || len(gopath) == 0 { 184 log.Fatal("GOPATH env not set") 185 } 186 return gopath 187 } 188 189 // MustSetGopath sets GOPATH=currentGopath after processing a path list, 190 // if any, then returns the set path. If GOPATH cannot be set, MustSetGopath 191 // exits. 192 func MustSetGopath(currentGopath string) string { 193 var ( 194 newGopath string 195 cwdInGopath bool 196 wd = MustGetwd() 197 ) 198 for _, newGopath = range strings.Split(currentGopath, ":") { 199 if strings.HasPrefix(filepath.Dir(wd), newGopath) { 200 cwdInGopath = true 201 break 202 } 203 } 204 if !cwdInGopath { 205 log.Fatalf("Project not in $GOPATH") 206 } 207 if err := os.Setenv(GoPathEnv, newGopath); err != nil { 208 log.Fatal(err) 209 } 210 return newGopath 211 } 212 213 var flagRe = regexp.MustCompile("(.* )?-v(.* )?") 214 215 // SetGoVerbose sets GOFLAGS="${GOFLAGS} -v" if GOFLAGS does not 216 // already contain "-v" to make "go" command output verbose. 217 func SetGoVerbose() error { 218 gf, ok := os.LookupEnv(GoFlagsEnv) 219 if !ok || len(gf) == 0 { 220 return os.Setenv(GoFlagsEnv, "-v") 221 } 222 if !flagRe.MatchString(gf) { 223 return os.Setenv(GoFlagsEnv, gf+" -v") 224 } 225 return nil 226 }