github.com/abayer/test-infra@v0.0.5/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 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 // JoinURL converts input (gs://foo, "bar") to gs://foo/bar 78 func JoinURL(urlPath, path string) (string, error) { 79 u, err := url.Parse(urlPath) 80 if err != nil { 81 return "", err 82 } 83 u.Path = filepath.Join(u.Path, path) 84 return u.String(), nil 85 } 86 87 // Pushd will Chdir() to dir and return a function to cd back to Getwd() 88 func Pushd(dir string) (func() error, error) { 89 old, err := os.Getwd() 90 if err != nil { 91 return nil, fmt.Errorf("failed to os.Getwd(): %v", err) 92 } 93 if err = os.Chdir(dir); err != nil { 94 return nil, err 95 } 96 return func() error { 97 return os.Chdir(old) 98 }, nil 99 } 100 101 // PushEnv pushes env=value and return a function that resets env 102 func PushEnv(env, value string) (func() error, error) { 103 prev, present := os.LookupEnv(env) 104 if err := os.Setenv(env, value); err != nil { 105 return nil, fmt.Errorf("could not set %s: %v", env, err) 106 } 107 return func() error { 108 if present { 109 return os.Setenv(env, prev) 110 } 111 return os.Unsetenv(env) 112 }, nil 113 } 114 115 // MigratedOption is an option that was an ENV that is now a --flag 116 type MigratedOption struct { 117 Env string // env associated with --flag 118 Option *string // Value of --flag 119 Name string // --flag name 120 SkipPush bool // Push option to env if false 121 } 122 123 // MigrateOptions reads value from ENV if --flag unset, optionally pushing to ENV 124 func MigrateOptions(m []MigratedOption) error { 125 for _, s := range m { 126 if *s.Option == "" { 127 // Jobs may not be using --foo instead of FOO just yet, so ease the transition 128 // TODO(fejta): require --foo instead of FOO 129 v := os.Getenv(s.Env) // expected Getenv 130 if v != "" { 131 // Tell people to use --foo=blah instead of FOO=blah 132 log.Printf("Please use kubetest %s=%s (instead of deprecated %s=%s)", s.Name, v, s.Env, v) 133 *s.Option = v 134 } 135 } 136 if s.SkipPush { 137 continue 138 } 139 // Script called by kubetest may expect these values to be set, so set them 140 // TODO(fejta): refactor the scripts below kubetest to use explicit config 141 if *s.Option == "" { 142 continue 143 } 144 if err := os.Setenv(s.Env, *s.Option); err != nil { 145 return fmt.Errorf("could not set %s=%s: %v", s.Env, *s.Option, err) 146 } 147 } 148 return nil 149 } 150 151 // AppendField will append prefix to the flag value. 152 // 153 // For example, AppendField(fields, "--foo", "bar") if fields is empty or does 154 // not contain a "--foo" it will simply append a "--foo=bar" value. 155 // Otherwise if fields contains "--foo=current" it will replace this value with 156 // "--foo=current-bar 157 func AppendField(fields []string, flag, prefix string) []string { 158 fields, cur, _ := ExtractField(fields, flag) 159 if len(cur) == 0 { 160 cur = prefix 161 } else { 162 cur += "-" + prefix 163 } 164 return append(fields, flag+"="+cur) 165 } 166 167 // SetFieldDefault sets the value of flag to val if flag is not present in fields. 168 // 169 // For example, SetFieldDefault(fields, "--foo", "bar") will append "--foo=bar" if 170 // fields is empty or does not include a "--foo" flag. 171 // It returns fields unchanged if "--foo" is present. 172 func SetFieldDefault(fields []string, flag, val string) []string { 173 fields, cur, present := ExtractField(fields, flag) 174 if !present { 175 cur = val 176 } 177 return append(fields, flag+"="+cur) 178 } 179 180 // ExtractField input ("--a=this --b=that --c=other", "--b") returns [--a=this, --c=other"], "that", true 181 // 182 // In other words, it will remove "--b" from fields and return the previous value of "--b" if it was set. 183 func ExtractField(fields []string, target string) ([]string, string, bool) { 184 f := []string{} 185 prefix := target + "=" 186 consumeNext := false 187 done := false 188 r := "" 189 for _, field := range fields { 190 switch { 191 case done: 192 f = append(f, field) 193 case consumeNext: 194 r = field 195 done = true 196 case field == target: 197 consumeNext = true 198 case strings.HasPrefix(field, prefix): 199 r = strings.SplitN(field, "=", 2)[1] 200 done = true 201 default: 202 f = append(f, field) 203 } 204 } 205 return f, r, done 206 } 207 208 // ExecError returns a string format of err including stderr if the 209 // err is an ExitError, useful for errors from e.g. exec.Cmd.Output(). 210 func ExecError(err error) string { 211 if ee, ok := err.(*exec.ExitError); ok { 212 return fmt.Sprintf("%v (output: %q)", err, string(ee.Stderr)) 213 } 214 return err.Error() 215 } 216 217 // EnsureExecutable sets the executable file mode bits, for all users, to ensure that we can execute a file 218 func EnsureExecutable(p string) error { 219 s, err := os.Stat(p) 220 if err != nil { 221 return fmt.Errorf("error doing stat on %q: %v", p, err) 222 } 223 if err := os.Chmod(p, s.Mode()|0111); err != nil { 224 return fmt.Errorf("error doing chmod on %q: %v", p, err) 225 } 226 return nil 227 } 228 229 // JSONForDebug returns a json representation of the value, or a string representation of an error 230 // It is useful for an easy implementation of fmt.Stringer 231 func JSONForDebug(o interface{}) string { 232 if o == nil { 233 return "nil" 234 } 235 v, err := json.Marshal(o) 236 if err != nil { 237 return fmt.Sprintf("error[%v]", err) 238 } 239 return string(v) 240 } 241 242 // FlushMem will try to reduce the memory usage of the container it is running in 243 // run this after a build 244 func FlushMem() { 245 log.Println("Flushing memory.") 246 // it's ok if these fail 247 // flush memory buffers 248 err := exec.Command("sync").Run() 249 if err != nil { 250 log.Printf("flushMem error (sync): %v", err) 251 } 252 // clear page cache 253 err = exec.Command("bash", "-c", "echo 1 > /proc/sys/vm/drop_caches").Run() 254 if err != nil { 255 log.Printf("flushMem error (page cache): %v", err) 256 } 257 }