github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/kubetest/util/util.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "go/build" 23 "log" 24 "net/url" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "strings" 29 ) 30 31 // K8s returns $GOPATH/src/k8s.io/... 32 func K8s(topdir string, parts ...string) string { 33 gopathList := filepath.SplitList(build.Default.GOPATH) 34 found := false 35 var kubedir string 36 for _, gopath := range gopathList { 37 kubedir = filepath.Join(gopath, "src", "k8s.io", topdir) 38 if _, err := os.Stat(kubedir); !os.IsNotExist(err) { 39 found = true 40 break 41 } 42 } 43 if !found { 44 // Default to the first item in GOPATH list. 45 kubedir = filepath.Join(gopathList[0], "src", "k8s.io", topdir) 46 log.Printf( 47 "Warning: Couldn't find directory src/k8s.io/%s under any of GOPATH %s, defaulting to %s", 48 topdir, build.Default.GOPATH, kubedir) 49 } 50 p := []string{kubedir} 51 p = append(p, parts...) 52 return filepath.Join(p...) 53 } 54 55 // AppendError does append(errs, err) if err != nil 56 func AppendError(errs []error, err error) []error { 57 if err != nil { 58 return append(errs, err) 59 } 60 return errs 61 } 62 63 // Home returns $HOME/part/part/part 64 func Home(parts ...string) string { 65 p := []string{os.Getenv("HOME")} 66 for _, a := range parts { 67 p = append(p, a) 68 } 69 return filepath.Join(p...) 70 } 71 72 // InsertPath does export PATH=path:$PATH 73 func InsertPath(path string) error { 74 return os.Setenv("PATH", fmt.Sprintf("%v:%v", path, os.Getenv("PATH"))) 75 } 76 77 // OptionalAbsPath returns an absolute path if the provided path wasn't empty, and otherwise 78 // returns an empty string. 79 func OptionalAbsPath(path string) (string, error) { 80 if path == "" { 81 return "", nil 82 } 83 84 return filepath.Abs(path) 85 } 86 87 // JoinURL converts input (gs://foo, "bar") to gs://foo/bar 88 func JoinURL(urlPath, path string) (string, error) { 89 u, err := url.Parse(urlPath) 90 if err != nil { 91 return "", err 92 } 93 u.Path = filepath.Join(u.Path, path) 94 return u.String(), nil 95 } 96 97 // Pushd will Chdir() to dir and return a function to cd back to Getwd() 98 func Pushd(dir string) (func() error, error) { 99 old, err := os.Getwd() 100 if err != nil { 101 return nil, fmt.Errorf("failed to os.Getwd(): %v", err) 102 } 103 if err = os.Chdir(dir); err != nil { 104 return nil, err 105 } 106 return func() error { 107 return os.Chdir(old) 108 }, nil 109 } 110 111 // PushEnv pushes env=value and return a function that resets env 112 func PushEnv(env, value string) (func() error, error) { 113 prev, present := os.LookupEnv(env) 114 if err := os.Setenv(env, value); err != nil { 115 return nil, fmt.Errorf("could not set %s: %v", env, err) 116 } 117 return func() error { 118 if present { 119 return os.Setenv(env, prev) 120 } 121 return os.Unsetenv(env) 122 }, nil 123 } 124 125 // MigratedOption is an option that was an ENV that is now a --flag 126 type MigratedOption struct { 127 Env string // env associated with --flag 128 Option *string // Value of --flag 129 Name string // --flag name 130 SkipPush bool // Push option to env if false 131 } 132 133 // MigrateOptions reads value from ENV if --flag unset, optionally pushing to ENV 134 func MigrateOptions(m []MigratedOption) error { 135 for _, s := range m { 136 if *s.Option == "" { 137 // Jobs may not be using --foo instead of FOO just yet, so ease the transition 138 // TODO(fejta): require --foo instead of FOO 139 v := os.Getenv(s.Env) // expected Getenv 140 if v != "" { 141 // Tell people to use --foo=blah instead of FOO=blah 142 log.Printf("Please use kubetest %s=%s (instead of deprecated %s=%s)", s.Name, v, s.Env, v) 143 *s.Option = v 144 } 145 } 146 if s.SkipPush { 147 continue 148 } 149 // Script called by kubetest may expect these values to be set, so set them 150 // TODO(fejta): refactor the scripts below kubetest to use explicit config 151 if *s.Option == "" { 152 continue 153 } 154 if err := os.Setenv(s.Env, *s.Option); err != nil { 155 return fmt.Errorf("could not set %s=%s: %v", s.Env, *s.Option, err) 156 } 157 } 158 return nil 159 } 160 161 // AppendField will append prefix to the flag value. 162 // 163 // For example, AppendField(fields, "--foo", "bar") if fields is empty or does 164 // not contain a "--foo" it will simply append a "--foo=bar" value. 165 // Otherwise if fields contains "--foo=current" it will replace this value with 166 // "--foo=current-bar 167 func AppendField(fields []string, flag, prefix string) []string { 168 fields, cur, _ := ExtractField(fields, flag) 169 if len(cur) == 0 { 170 cur = prefix 171 } else { 172 cur += "-" + prefix 173 } 174 return append(fields, flag+"="+cur) 175 } 176 177 // SetFieldDefault sets the value of flag to val if flag is not present in fields. 178 // 179 // For example, SetFieldDefault(fields, "--foo", "bar") will append "--foo=bar" if 180 // fields is empty or does not include a "--foo" flag. 181 // It returns fields unchanged if "--foo" is present. 182 func SetFieldDefault(fields []string, flag, val string) []string { 183 fields, cur, present := ExtractField(fields, flag) 184 if !present { 185 cur = val 186 } 187 return append(fields, flag+"="+cur) 188 } 189 190 // ExtractField input ("--a=this --b=that --c=other", "--b") returns [--a=this, --c=other"], "that", true 191 // 192 // In other words, it will remove "--b" from fields and return the previous value of "--b" if it was set. 193 func ExtractField(fields []string, target string) ([]string, string, bool) { 194 f := []string{} 195 prefix := target + "=" 196 consumeNext := false 197 done := false 198 r := "" 199 for _, field := range fields { 200 switch { 201 case done: 202 f = append(f, field) 203 case consumeNext: 204 r = field 205 done = true 206 case field == target: 207 consumeNext = true 208 case strings.HasPrefix(field, prefix): 209 r = strings.SplitN(field, "=", 2)[1] 210 done = true 211 default: 212 f = append(f, field) 213 } 214 } 215 return f, r, done 216 } 217 218 // ExecError returns a string format of err including stderr if the 219 // err is an ExitError, useful for errors from e.g. exec.Cmd.Output(). 220 func ExecError(err error) string { 221 if ee, ok := err.(*exec.ExitError); ok { 222 return fmt.Sprintf("%v (output: %q)", err, string(ee.Stderr)) 223 } 224 return err.Error() 225 } 226 227 // EnsureExecutable sets the executable file mode bits, for all users, to ensure that we can execute a file 228 func EnsureExecutable(p string) error { 229 s, err := os.Stat(p) 230 if err != nil { 231 return fmt.Errorf("error doing stat on %q: %v", p, err) 232 } 233 if err := os.Chmod(p, s.Mode()|0111); err != nil { 234 return fmt.Errorf("error doing chmod on %q: %v", p, err) 235 } 236 return nil 237 } 238 239 // JSONForDebug returns a json representation of the value, or a string representation of an error 240 // It is useful for an easy implementation of fmt.Stringer 241 func JSONForDebug(o interface{}) string { 242 if o == nil { 243 return "nil" 244 } 245 v, err := json.Marshal(o) 246 if err != nil { 247 return fmt.Sprintf("error[%v]", err) 248 } 249 return string(v) 250 } 251 252 // FlushMem will try to reduce the memory usage of the container it is running in 253 // run this after a build 254 func FlushMem() { 255 log.Println("Flushing memory.") 256 // it's ok if these fail 257 // flush memory buffers 258 err := exec.Command("sync").Run() 259 if err != nil { 260 log.Printf("flushMem error (sync): %v", err) 261 } 262 // clear page cache 263 err = exec.Command("bash", "-c", "echo 1 > /proc/sys/vm/drop_caches").Run() 264 if err != nil { 265 log.Printf("flushMem error (page cache): %v", err) 266 } 267 }